<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
	<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=OscarFox</id>
	<title>Алговики - Вклад [ru]</title>
	<link rel="self" type="application/atom+xml" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=OscarFox"/>
	<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/ru/%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/OscarFox"/>
	<updated>2026-10-22T16:38:26Z</updated>
	<subtitle>Вклад</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30385</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30385"/>
		<updated>2025-12-21T14:45:34Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Описание подзадачи (CPA) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = UV^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD может быть различной в зависимости от используемой реализации.&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «[https://parallel.ru/cluster/lomonosov2.html Ломоносов-2]». Для этого была исследована авторская реализация алгоритма, выполненная с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
* Размерность входа: от 2 включительно до 202 включительно с шагом 10.&lt;br /&gt;
* Число процессов: [1, 2, 4, 8, 16, 32, 64, 92].&lt;br /&gt;
&lt;br /&gt;
[[Файл:Time scaled.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких итераций алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:Time gpa corrected.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких итераций алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Efficiency corrected.png|700px|left|мини|Рис.7. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup corrected.png|700px|right|мини|Рис.8. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
По графикам можно наблюдать сверхлинейное ускорение в случае относительно небольшого числа процессоров. Это может быть связано с активным использованием кэша. Так как GPA в каждом процессе работает только с локальным набором форм (может быть небольшим по объему), то формы могут быть сохранены в кэше, а не быть загруженными из оперативной памяти и затем быть снова выгруженными (что может происходить по несколько раз в последовательной реализации).&lt;br /&gt;
&lt;br /&gt;
Максимальная эффективность составила &amp;lt;math&amp;gt;611.8\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=202&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=4&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Минимальная эффективность составила &amp;lt;math&amp;gt;0.0022\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=12&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=92&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Исходный код, описание параметров окружения и данные в числовом виде доступны в [https://github.com/oscar-foxtrot/HPC-GPA/tree/main GitHub-репозитории].&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Существует последовательная реализация GPA в библиотеке Prince&amp;lt;ref&amp;gt;&#039;&#039;Prince Python library&#039;&#039; // https://maxhalford.github.io/prince/&amp;lt;/ref&amp;gt; для языка программирования Python.&lt;br /&gt;
&lt;br /&gt;
Также написаны [https://github.com/oscar-foxtrot/HPC-GPA/tree/main авторские реализации] последовательного (на ЯП Python и C++) и параллельного алгоритма (на C++) Generalized Procrustes Analysis.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30384</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30384"/>
		<updated>2025-12-11T10:21:11Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD может быть различной в зависимости от используемой реализации.&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «[https://parallel.ru/cluster/lomonosov2.html Ломоносов-2]». Для этого была исследована авторская реализация алгоритма, выполненная с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
* Размерность входа: от 2 включительно до 202 включительно с шагом 10.&lt;br /&gt;
* Число процессов: [1, 2, 4, 8, 16, 32, 64, 92].&lt;br /&gt;
&lt;br /&gt;
[[Файл:Time scaled.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких итераций алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:Time gpa corrected.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких итераций алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Efficiency corrected.png|700px|left|мини|Рис.7. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup corrected.png|700px|right|мини|Рис.8. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
По графикам можно наблюдать сверхлинейное ускорение в случае относительно небольшого числа процессоров. Это может быть связано с активным использованием кэша. Так как GPA в каждом процессе работает только с локальным набором форм (может быть небольшим по объему), то формы могут быть сохранены в кэше, а не быть загруженными из оперативной памяти и затем быть снова выгруженными (что может происходить по несколько раз в последовательной реализации).&lt;br /&gt;
&lt;br /&gt;
Максимальная эффективность составила &amp;lt;math&amp;gt;611.8\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=202&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=4&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Минимальная эффективность составила &amp;lt;math&amp;gt;0.0022\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=12&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=92&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Исходный код, описание параметров окружения и данные в числовом виде доступны в [https://github.com/oscar-foxtrot/HPC-GPA/tree/main GitHub-репозитории].&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Существует последовательная реализация GPA в библиотеке Prince&amp;lt;ref&amp;gt;&#039;&#039;Prince Python library&#039;&#039; // https://maxhalford.github.io/prince/&amp;lt;/ref&amp;gt; для языка программирования Python.&lt;br /&gt;
&lt;br /&gt;
Также написаны [https://github.com/oscar-foxtrot/HPC-GPA/tree/main авторские реализации] последовательного (на ЯП Python и C++) и параллельного алгоритма (на C++) Generalized Procrustes Analysis.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30383</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30383"/>
		<updated>2025-12-11T10:18:45Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD может быть различной в зависимости от используемой реализации.&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «[https://parallel.ru/cluster/lomonosov2.html Ломоносов-2]». Для этого была исследована авторская реализация алгоритма, выполненная с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
* Размерность входа: от 2 включительно до 202 включительно с шагом 10.&lt;br /&gt;
* Число процессов: [1, 2, 4, 8, 16, 32, 64, 92].&lt;br /&gt;
&lt;br /&gt;
[[Файл:Time scaled.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:Time gpa corrected.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Efficiency corrected.png|700px|left|мини|Рис.7. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup corrected.png|700px|right|мини|Рис.8. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
По графикам можно наблюдать сверхлинейное ускорение в случае относительно небольшого числа процессоров. Это может быть связано с активным использованием кэша. Так как GPA в каждом процессе работает только с локальным набором форм (может быть небольшим по объему), то формы могут быть сохранены в кэше, а не быть загруженными из оперативной памяти и затем быть снова выгруженными (что может происходить по несколько раз в последовательной реализации).&lt;br /&gt;
&lt;br /&gt;
Максимальная эффективность составила &amp;lt;math&amp;gt;611.8\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=202&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=4&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Минимальная эффективность составила &amp;lt;math&amp;gt;0.0022\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=12&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=92&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Исходный код, описание параметров окружения и данные в числовом виде доступны в [https://github.com/oscar-foxtrot/HPC-GPA/tree/main GitHub-репозитории].&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Существует последовательная реализация GPA в библиотеке Prince&amp;lt;ref&amp;gt;&#039;&#039;Prince Python library&#039;&#039; // https://maxhalford.github.io/prince/&amp;lt;/ref&amp;gt; для языка программирования Python.&lt;br /&gt;
&lt;br /&gt;
Также написаны [https://github.com/oscar-foxtrot/HPC-GPA/tree/main авторские реализации] последовательного (на ЯП Python и C++) и параллельного алгоритма (на C++) Generalized Procrustes Analysis.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Speedup_corrected.png&amp;diff=30382</id>
		<title>Файл:Speedup corrected.png</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Speedup_corrected.png&amp;diff=30382"/>
		<updated>2025-12-11T10:18:32Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;График зависимости ускорения от параметров&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Time_gpa_corrected.png&amp;diff=30381</id>
		<title>Файл:Time gpa corrected.png</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Time_gpa_corrected.png&amp;diff=30381"/>
		<updated>2025-12-11T10:17:26Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;График зависимости времени выполнения от числа параметров&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Time_scaled.png&amp;diff=30380</id>
		<title>Файл:Time scaled.png</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Time_scaled.png&amp;diff=30380"/>
		<updated>2025-12-11T10:16:22Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Увеличенная по масштабу версия графика зависимости времени выполнения от параметров&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30379</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30379"/>
		<updated>2025-12-11T10:06:44Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD может быть различной в зависимости от используемой реализации.&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «[https://parallel.ru/cluster/lomonosov2.html Ломоносов-2]». Для этого была исследована авторская реализация алгоритма, выполненная с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
* Размерность входа: от 2 включительно до 202 включительно с шагом 10.&lt;br /&gt;
* Число процессов: [1, 2, 4, 8, 16, 32, 64, 92].&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Efficiency corrected.png|700px|left|мини|Рис.7. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup gpa.png|700px|right|мини|Рис.8. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
По графикам можно наблюдать сверхлинейное ускорение в случае относительно небольшого числа процессоров. Это может быть связано с активным использованием кэша. Так как GPA в каждом процессе работает только с локальным набором форм (может быть небольшим по объему), то формы могут быть сохранены в кэше, а не быть загруженными из оперативной памяти и затем быть снова выгруженными (что может происходить по несколько раз в последовательной реализации).&lt;br /&gt;
&lt;br /&gt;
Максимальная эффективность составила &amp;lt;math&amp;gt;611.8\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=202&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=4&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Минимальная эффективность составила &amp;lt;math&amp;gt;0.0022\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=12&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=92&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Исходный код, описание параметров окружения и данные в числовом виде доступны в [https://github.com/oscar-foxtrot/HPC-GPA/tree/main GitHub-репозитории].&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Существует последовательная реализация GPA в библиотеке Prince&amp;lt;ref&amp;gt;&#039;&#039;Prince Python library&#039;&#039; // https://maxhalford.github.io/prince/&amp;lt;/ref&amp;gt; для языка программирования Python.&lt;br /&gt;
&lt;br /&gt;
Также написаны [https://github.com/oscar-foxtrot/HPC-GPA/tree/main авторские реализации] последовательного (на ЯП Python и C++) и параллельного алгоритма (на C++) Generalized Procrustes Analysis.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Efficiency_corrected.png&amp;diff=30378</id>
		<title>Файл:Efficiency corrected.png</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Efficiency_corrected.png&amp;diff=30378"/>
		<updated>2025-12-11T10:06:15Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;График зависимости эффективности распараллеливания от числа процессоров.&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30377</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30377"/>
		<updated>2025-12-10T20:21:16Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Существующие реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD может быть различной в зависимости от используемой реализации.&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «[https://parallel.ru/cluster/lomonosov2.html Ломоносов-2]». Для этого была исследована авторская реализация алгоритма, выполненная с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
* Размерность входа: от 2 включительно до 202 включительно с шагом 10.&lt;br /&gt;
* Число процессов: [1, 2, 4, 8, 16, 32, 64, 92].&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Eff.png|700px|left|мини|Рис.7. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup gpa.png|700px|right|мини|Рис.8. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
По графикам можно наблюдать сверхлинейное ускорение в случае относительно небольшого числа процессоров. Это может быть связано с активным использованием кэша. Так как GPA в каждом процессе работает только с локальным набором форм (может быть небольшим по объему), то формы могут быть сохранены в кэше, а не быть загруженными из оперативной памяти и затем быть снова выгруженными (что может происходить по несколько раз в последовательной реализации).&lt;br /&gt;
&lt;br /&gt;
Максимальная эффективность составила &amp;lt;math&amp;gt;611.8\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=202&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=4&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Минимальная эффективность составила &amp;lt;math&amp;gt;0.0022\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=12&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=92&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Исходный код, описание параметров окружения и данные в числовом виде доступны в [https://github.com/oscar-foxtrot/HPC-GPA/tree/main GitHub-репозитории].&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Существует последовательная реализация GPA в библиотеке Prince&amp;lt;ref&amp;gt;&#039;&#039;Prince Python library&#039;&#039; // https://maxhalford.github.io/prince/&amp;lt;/ref&amp;gt; для языка программирования Python.&lt;br /&gt;
&lt;br /&gt;
Также написаны [https://github.com/oscar-foxtrot/HPC-GPA/tree/main авторские реализации] последовательного (на ЯП Python и C++) и параллельного алгоритма (на C++) Generalized Procrustes Analysis.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30374</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30374"/>
		<updated>2025-12-10T15:33:20Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Существующие реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD может быть различной в зависимости от используемой реализации.&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «[https://parallel.ru/cluster/lomonosov2.html Ломоносов-2]». Для этого была исследована авторская реализация алгоритма, выполненная с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
* Размерность входа: от 2 включительно до 202 включительно с шагом 10.&lt;br /&gt;
* Число процессов: [1, 2, 4, 8, 16, 32, 64, 92].&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Eff.png|700px|left|мини|Рис.7. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup gpa.png|700px|right|мини|Рис.8. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
По графикам можно наблюдать сверхлинейное ускорение в случае относительно небольшого числа процессоров. Это может быть связано с активным использованием кэша. Так как GPA в каждом процессе работает только с локальным набором форм (может быть небольшим по объему), то формы могут быть сохранены в кэше, а не быть загруженными из оперативной памяти и затем быть снова выгруженными (что может происходить по несколько раз в последовательной реализации).&lt;br /&gt;
&lt;br /&gt;
Максимальная эффективность составила &amp;lt;math&amp;gt;611.8\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=202&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=4&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Минимальная эффективность составила &amp;lt;math&amp;gt;0.0022\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=12&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=92&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Исходный код, описание параметров окружения и данные в числовом виде доступны в [https://github.com/oscar-foxtrot/HPC-GPA/tree/main GitHub-репозитории].&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Существует последовательная реализация GPA в библиотеке Prince&amp;lt;ref&amp;gt;&#039;&#039;Prince Python library&#039;&#039; // https://maxhalford.github.io/prince/&amp;lt;/ref&amp;gt; для языка программирования Python.&lt;br /&gt;
&lt;br /&gt;
Также написаны [https://github.com/oscar-foxtrot/HPC-GPA/tree/main авторская реализация] последовательного (на ЯП Python и C++) и параллельного алгоритма (на C++) Generalized Procrustes Analysis.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30373</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30373"/>
		<updated>2025-12-10T15:15:03Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD может быть различной в зависимости от используемой реализации.&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «[https://parallel.ru/cluster/lomonosov2.html Ломоносов-2]». Для этого была исследована авторская реализация алгоритма, выполненная с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
* Размерность входа: от 2 включительно до 202 включительно с шагом 10.&lt;br /&gt;
* Число процессов: [1, 2, 4, 8, 16, 32, 64, 92].&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Eff.png|700px|left|мини|Рис.7. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup gpa.png|700px|right|мини|Рис.8. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
По графикам можно наблюдать сверхлинейное ускорение в случае относительно небольшого числа процессоров. Это может быть связано с активным использованием кэша. Так как GPA в каждом процессе работает только с локальным набором форм (может быть небольшим по объему), то формы могут быть сохранены в кэше, а не быть загруженными из оперативной памяти и затем быть снова выгруженными (что может происходить по несколько раз в последовательной реализации).&lt;br /&gt;
&lt;br /&gt;
Максимальная эффективность составила &amp;lt;math&amp;gt;611.8\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=202&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=4&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Минимальная эффективность составила &amp;lt;math&amp;gt;0.0022\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=12&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=92&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Исходный код, описание параметров окружения и данные в числовом виде доступны в [https://github.com/oscar-foxtrot/HPC-GPA/tree/main GitHub-репозитории].&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30372</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30372"/>
		<updated>2025-12-10T15:11:26Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Ресурс параллелизма алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD может быть различной в зависимости от используемой реализации.&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «[https://parallel.ru/cluster/lomonosov2.html Ломоносов-2]». Для этого была исследована авторская реализация алгоритма, выполненная с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
* Размерность входа: от 2 включительно до 202 включительно с шагом 10.&lt;br /&gt;
* Число процессов: [1, 2, 4, 8, 16, 32, 64, 92].&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Eff.png|700px|left|мини|Рис.7. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup gpa.png|700px|right|мини|Рис.8. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
По графикам можно наблюдать сверхлинейное ускорение в случае относительно небольшого числа процессоров. Это может быть связано с активным использованием кэша. Так как GPA в каждом процессе работает только с локальным набором форм (может быть небольшим по объему), то формы могут быть сохранены в кэше, а не быть загруженными из оперативной памяти и затем быть снова выгруженными, причем по несколько раз (что имеет место в последовательной реализации).&lt;br /&gt;
&lt;br /&gt;
Максимальная эффективность составила &amp;lt;math&amp;gt;611.8\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=202&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=4&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Минимальная эффективность составила &amp;lt;math&amp;gt;0.0022\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=12&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=92&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Исходный код, описание параметров окружения и данные в числовом виде доступны в [https://github.com/oscar-foxtrot/HPC-GPA/tree/main GitHub-репозитории].&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30366</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30366"/>
		<updated>2025-12-10T07:16:25Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «[https://parallel.ru/cluster/lomonosov2.html Ломоносов-2]». Для этого была исследована авторская реализация алгоритма, выполненная с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
* Размерность входа: от 2 включительно до 202 включительно с шагом 10.&lt;br /&gt;
* Число процессов: [1, 2, 4, 8, 16, 32, 64, 92].&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Eff.png|700px|left|мини|Рис.7. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup gpa.png|700px|right|мини|Рис.8. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
По графикам можно наблюдать сверхлинейное ускорение в случае относительно небольшого числа процессоров. Это может быть связано с активным использованием кэша. Так как GPA в каждом процессе работает только с локальным набором форм (может быть небольшим по объему), то формы могут быть сохранены в кэше, а не быть загруженными из оперативной памяти и затем быть снова выгруженными, причем по несколько раз (что имеет место в последовательной реализации).&lt;br /&gt;
&lt;br /&gt;
Максимальная эффективность составила &amp;lt;math&amp;gt;611.8\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=202&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=4&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Минимальная эффективность составила &amp;lt;math&amp;gt;0.0022\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=12&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=92&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Исходный код, описание параметров окружения и данные в числовом виде доступны в [https://github.com/oscar-foxtrot/HPC-GPA/tree/main GitHub-репозитории].&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30365</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30365"/>
		<updated>2025-12-10T07:14:29Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «[https://parallel.ru/cluster/lomonosov2.html Ломоносов-2]». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
* Размерность входа: от 2 включительно до 202 включительно с шагом 10.&lt;br /&gt;
* Число процессов: [1, 2, 4, 8, 16, 32, 64, 92].&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Eff.png|700px|left|мини|Рис.7. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup gpa.png|700px|right|мини|Рис.8. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
По графикам можно наблюдать сверхлинейное ускорение в случае относительно небольшого числа процессоров. Это может быть связано с активным использованием кэша. Так как GPA в каждом процессе работает только с локальным набором форм (может быть небольшим по объему), то формы могут быть сохранены в кэше, а не быть загруженными из оперативной памяти и затем быть снова выгруженными, причем по несколько раз (что имеет место в последовательной реализации).&lt;br /&gt;
&lt;br /&gt;
Максимальная эффективность составила &amp;lt;math&amp;gt;611.8\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=202&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=4&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Минимальная эффективность составила &amp;lt;math&amp;gt;0.0022\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=12&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=92&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Исходный код, описание параметров окружения и данные в числовом виде доступны в [https://github.com/oscar-foxtrot/HPC-GPA/tree/main GitHub-репозитории].&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30364</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30364"/>
		<updated>2025-12-10T07:12:25Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «[https://parallel.ru/cluster/lomonosov2.html Ломоносов-2]». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
* Размерность входа: от 2 включительно до 202 включительно с шагом 10.&lt;br /&gt;
* Число процессов: [1, 2, 4, 8, 16, 32, 64, 92].&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Eff.png|700px|left|мини|Рис.7. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup gpa.png|700px|right|мини|Рис.8. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
По графикам можно наблюдать сверхлинейное ускорение в случае относительно небольшого числа процессоров. Это может быть связано с активным использованием кэша. Так как GPA в каждом процессе работает только с локальным набором форм (может быть небольшим по объему), то формы могут быть сохранены в кэше, а не быть загруженными из оперативной памяти и затем быть снова выгруженными, причем по несколько раз (что имеет место в последовательной реализации).&lt;br /&gt;
&lt;br /&gt;
Максимальная эффективность составила &amp;lt;math&amp;gt;611.8\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=202&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=4&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Минимальная эффективность составила &amp;lt;math&amp;gt;0.0022\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=12&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=92&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Исходный код и данные в числовом виде доступны в [https://github.com/oscar-foxtrot/HPC-GPA/tree/main GitHub-репозитории].&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30363</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30363"/>
		<updated>2025-12-10T07:04:25Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «[https://parallel.ru/cluster/lomonosov2.html Ломоносов-2]». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
* Размерность входа: от 2 включительно до 202 включительно с шагом 10.&lt;br /&gt;
* Число процессов: [1, 2, 4, 8, 16, 32, 64, 92].&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Eff.png|700px|left|мини|Рис.7. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup gpa.png|700px|right|мини|Рис.8. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
По графикам можно наблюдать сверхлинейное ускорение в случае относительно небольшого числа процессоров. Это может быть связано с активным использованием кэша. Так как GPA в каждом процессе работает только с локальным набором форм (может быть небольшим по объему), то формы могут быть сохранены в кэше, а не быть загруженными из оперативной памяти и затем быть снова выгруженными, причем по несколько раз (что имеет место в последовательной реализации).&lt;br /&gt;
&lt;br /&gt;
Максимальная эффективность составила &amp;lt;math&amp;gt;611.8\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=202&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=4&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Минимальная эффективность составила &amp;lt;math&amp;gt;0.0022\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=12&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=92&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30362</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30362"/>
		<updated>2025-12-10T06:32:42Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «[https://parallel.ru/cluster/lomonosov2.html Ломоносов-2]». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Eff.png|700px|left|мини|Рис.7. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup gpa.png|700px|right|мини|Рис.8. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
По графикам можно наблюдать сверхлинейное ускорение в случае относительно небольшого числа процессоров. Это может быть связано с активным использованием кэша. Так как GPA в каждом процессе работает только с локальным набором форм (может быть небольшим по объему), то формы могут быть сохранены в кэше, а не быть загруженными из оперативной памяти и затем быть снова выгруженными, причем по несколько раз (что имеет место в последовательной реализации).&lt;br /&gt;
&lt;br /&gt;
Максимальная эффективность составила &amp;lt;math&amp;gt;611.8\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=202&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=4&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Минимальная эффективность составила &amp;lt;math&amp;gt;0.0022\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=12&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=92&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30361</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30361"/>
		<updated>2025-12-10T06:32:28Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «[https://parallel.ru/cluster/lomonosov2.html | Ломоносов-2]». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Eff.png|700px|left|мини|Рис.7. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup gpa.png|700px|right|мини|Рис.8. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
По графикам можно наблюдать сверхлинейное ускорение в случае относительно небольшого числа процессоров. Это может быть связано с активным использованием кэша. Так как GPA в каждом процессе работает только с локальным набором форм (может быть небольшим по объему), то формы могут быть сохранены в кэше, а не быть загруженными из оперативной памяти и затем быть снова выгруженными, причем по несколько раз (что имеет место в последовательной реализации).&lt;br /&gt;
&lt;br /&gt;
Максимальная эффективность составила &amp;lt;math&amp;gt;611.8\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=202&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=4&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Минимальная эффективность составила &amp;lt;math&amp;gt;0.0022\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=12&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=92&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30360</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30360"/>
		<updated>2025-12-10T06:32:16Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «[https://parallel.ru/cluster/lomonosov2.html| Ломоносов-2]». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Eff.png|700px|left|мини|Рис.7. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup gpa.png|700px|right|мини|Рис.8. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
По графикам можно наблюдать сверхлинейное ускорение в случае относительно небольшого числа процессоров. Это может быть связано с активным использованием кэша. Так как GPA в каждом процессе работает только с локальным набором форм (может быть небольшим по объему), то формы могут быть сохранены в кэше, а не быть загруженными из оперативной памяти и затем быть снова выгруженными, причем по несколько раз (что имеет место в последовательной реализации).&lt;br /&gt;
&lt;br /&gt;
Максимальная эффективность составила &amp;lt;math&amp;gt;611.8\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=202&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=4&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Минимальная эффективность составила &amp;lt;math&amp;gt;0.0022\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=12&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=92&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30359</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30359"/>
		<updated>2025-12-10T06:24:54Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Eff.png|700px|left|мини|Рис.7. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup gpa.png|700px|right|мини|Рис.8. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
По графикам можно наблюдать сверхлинейное ускорение в случае относительно небольшого числа процессоров. Это может быть связано с активным использованием кэша. Так как GPA в каждом процессе работает только с локальным набором форм (может быть небольшим по объему), то формы могут быть сохранены в кэше, а не быть загруженными из оперативной памяти и затем быть снова выгруженными, причем по несколько раз (что имеет место в последовательной реализации).&lt;br /&gt;
&lt;br /&gt;
Максимальная эффективность составила &amp;lt;math&amp;gt;611.8\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=202&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=4&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Минимальная эффективность составила &amp;lt;math&amp;gt;0.0022\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=12&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=92&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30358</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30358"/>
		<updated>2025-12-10T06:23:59Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Eff.png|700px|left|мини|Рис.7. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup gpa.png|700px|right|мини|Рис.8. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
По графикам можно наблюдать сверхлинейное ускорение в случае относительно небольшого числа процессоров. Это может быть связано с активным использованием кэша. Так как GPA в каждом процессе работает только с локальным набором форм (может быть небольшим по объему), то формы могут быть сохранены в кэше, а не быть загруженными из оперативной памяти и затем быть снова выгруженными, причем по несколько раз (что имеет место в последовательной реализации).&lt;br /&gt;
&lt;br /&gt;
Максимальная эффективность составила &amp;lt;math&amp;gt;611.8\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=12&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=92&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Минимальная эффективность составила &amp;lt;math&amp;gt;0.0022\%&amp;lt;/math&amp;gt; в случае &amp;lt;math&amp;gt;n=202&amp;lt;/math&amp;gt;, и при числе процессов &amp;lt;math&amp;gt;p=4&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30357</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30357"/>
		<updated>2025-12-10T05:49:42Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Eff.png|700px|left|мини|Рис.7. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup gpa.png|700px|right|мини|Рис.8. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
По графикам можно наблюдать сверхлинейное ускорение в случае относительно небольшого числа процессоров. Это может быть связано с активным использованием кэша. Так как GPA в каждом процессе работает только с локальным набором форм (может быть небольшим по объему), то формы могут быть сохранены в кэше, а не быть загруженными из оперативной памяти и затем быть снова выгруженными, причем по несколько раз (что имеет место в последовательной реализации).&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30356</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30356"/>
		<updated>2025-12-10T05:49:21Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Eff.png|700px|left|мини|Рис.7. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup gpa.png|700px|right|мини|Рис.8. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
По графикам можно наблюдать сверхлинейное ускорение в случае относительно небольшого числа процессоров. Это может быть связано с активным использованием кэша. Так как GPA в каждом процессе работает только с локальным набором форм (может быть небольшим по объему), то формы могут быть сохранены в кэше, нежели быть загруженными из оперативной памяти и затем быть снова выгруженными, причем по несколько раз (что имеет место в последовательной реализации).&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30355</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30355"/>
		<updated>2025-12-10T05:47:54Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Eff.png|700px|left|мини|Рис.7. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup gpa.png|700px|right|мини|Рис.8. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
По графикам можно наблюдать сверхлинейное ускорение в случае относительно небольшого числа процессоров. Это может быть связано с активным использованием кэша. Так как GPA в каждом процессе работает только с локальным набором форм (может быть небольшим по объему), то формы могут быть сохранены в кэше, нежели быть загружены из оперативной памяти и затем снова выгружены по нескольку раз (что имеет место в последовательной реализации).&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30354</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30354"/>
		<updated>2025-12-10T05:47:24Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Eff.png|700px|left|мини|Рис.7. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup gpa.png|700px|right|мини|Рис.8. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
По графикам можно наблюдать сверхлинейное ускорение в случае относительно небольшого числа процессоров. Это может быть связано с активным использованием кэша. Так как GPA в каждом процессе работает только с локальным набором форм (может быть небольшим по объему), то формы могут быть сохранены в кэше, нежели быть загружены из оперативной памяти и затем снова выгружены несколько раз, как в последовательной реализации.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30353</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30353"/>
		<updated>2025-12-10T05:42:16Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Eff.png|700px|left|мини|Рис.7. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup gpa.png|700px|right|мини|Рис.8. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30352</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30352"/>
		<updated>2025-12-10T05:40:12Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt; - ускорение, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Eff.png|700px|left|мини|Рис.7. График зависимости ускорения от объема входных данных и числа процессов]]&lt;br /&gt;
[[Файл:Speedup gpa.png|700px|right|мини|Рис.8. График зависимости эффективности распараллеливания от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Speedup_gpa.png&amp;diff=30351</id>
		<title>Файл:Speedup gpa.png</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Speedup_gpa.png&amp;diff=30351"/>
		<updated>2025-12-10T05:36:07Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ускорение параллельной реализции GPA&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Eff.png&amp;diff=30350</id>
		<title>Файл:Eff.png</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Eff.png&amp;diff=30350"/>
		<updated>2025-12-10T05:35:08Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Эффективность распараллеливания&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30349</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30349"/>
		<updated>2025-12-10T05:08:16Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;. Количество итераций равно двум (не включая нулевую).&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30348</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30348"/>
		<updated>2025-12-10T04:37:27Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; // https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30347</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30347"/>
		<updated>2025-12-10T04:37:09Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера (многоядерные процессоры с подержкой гипертрединга) и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30346</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30346"/>
		<updated>2025-12-10T04:35:20Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма, конфигурации вычислительных узлов суперкомпьютера и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30345</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30345"/>
		<updated>2025-12-10T04:34:50Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30344</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30344"/>
		<updated>2025-12-10T04:33:41Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI, т.к. с учетом наличия достаточно большого числа ядер на одном физическом процессоре при ограничении на число нитей OpenMP ожидается, что каждый новый порожденный процесс MPI можно считать самостоятельной единицей выполнения.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы на &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорах.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30343</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30343"/>
		<updated>2025-12-10T04:32:35Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI, т.к. с учетом наличия достаточно большого числа ядер на одном физическом процессоре при ограничении на число нитей OpenMP ожидается, что каждый новый порожденный процесс MPI можно считать самостоятельной единицей выполнения.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорами&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30342</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30342"/>
		<updated>2025-12-10T04:32:16Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;&amp;quot;Eigen&amp;quot; C++ library&#039;&#039; https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI, т.к. с учетом наличия достаточно большого числа ядер на одном физическом процессоре при ограничении на число нитей OpenMP ожидается, что каждый новый порожденный процесс MPI можно считать самостоятельной единицей выполнения.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорами&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30341</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30341"/>
		<updated>2025-12-10T04:31:57Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen C++ library&#039;&#039; https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI, т.к. с учетом наличия достаточно большого числа ядер на одном физическом процессоре при ограничении на число нитей OpenMP ожидается, что каждый новый порожденный процесс MPI можно считать самостоятельной единицей выполнения.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорами&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30340</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30340"/>
		<updated>2025-12-10T04:31:12Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма и реализации (используется библиотека Eigen&amp;lt;ref&amp;gt;&#039;&#039;Eigen&#039;&#039; https://libeigen.gitlab.io/&amp;lt;/ref&amp;gt; с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI, т.к. с учетом наличия достаточно большого числа ядер на одном физическом процессоре при ограничении на число нитей OpenMP ожидается, что каждый новый порожденный процесс MPI можно считать самостоятельной единицей выполнения.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорами&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30339</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30339"/>
		<updated>2025-12-10T04:29:56Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма и реализации (используется библиотека Eigen с установленным ограничением на распараллеливание с помощью OpenMP в 3 нити - это значение было получено опытным путем как оптимальное) число процессоров можно смоделировать числом порожденных процессов MPI, т.к. с учетом наличия достаточно большого числа ядер на одном физическом процессоре при ограничении на число нитей OpenMP ожидается, что каждый новый порожденный процесс MPI можно считать самостоятельной единицей выполнения.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорами&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30338</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30338"/>
		<updated>2025-12-10T04:28:12Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессоров, в то время как при небольших матрицах и небольшом числе форм выделение дополнительных процессоров лишь увеличивает накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессоров. В условиях исследуемого алгоритма и реализации (используется библиотека Eigen ) число процессоров можно смоделировать числом порожденных процессов MPI, т.к. с учетом наличия достаточно большого числа ядер на одном физическом процессоре при ограничении на число нитей OpenMP ожидается, что каждый новый порожденный процесс MPI можно считать самостоятельной единицей выполнения.&lt;br /&gt;
&lt;br /&gt;
Эффективность распараллеливания есть величина &amp;lt;math&amp;gt;E = S/p&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;S = \frac{T_1}{T_p}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - число используемых процессоров, и &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt; - время работы программы с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; процессорами&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30337</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30337"/>
		<updated>2025-12-10T03:53:29Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах. Соответственно, под размерностью матрицы понимается число &amp;lt;math&amp;gt;n=d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессов MPI, в то время как при небольших матрицах и небольшом числе форм дополнительные процессы MPI лишь увеличивают накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессов.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30336</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30336"/>
		<updated>2025-12-10T03:52:01Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающего размерность форм и их число, а также различным числом процессов.&lt;br /&gt;
&lt;br /&gt;
Далее представлены два графика зависимости времени выполнения от объема входных данных и числа процессов в различных масштабах. Важно, что здесь для наглядности представления положено &amp;lt;math&amp;gt;m = n = d&amp;lt;/math&amp;gt;, т.е. число входных форм совпадает с числом строк и столбцов в этих формах.&lt;br /&gt;
&lt;br /&gt;
[[Файл:GPA Execution time 1.png|700px|left|мини|Рис.5. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов (увеличенный масштаб)]]&lt;br /&gt;
[[Файл:GPA execution time 2.png|700px|right|мини|Рис.6. График зависимости времени выполнения нескольких алгоритма от объема входных данных и числа процессов]]&lt;br /&gt;
&lt;br /&gt;
Из графиков видно, что при большом объеме входных данных имеется выигрыш по времени выполнения при росте числа процессов MPI, в то время как при небольших матрицах и небольшом числе форм дополнительные процессы MPI лишь увеличивают накладные расходы, вследствие чего время выполнения растет. Также очевидна закономерность, при которой время выполнения в среднем растет с увеличением объема входных данных при неизменном числе процессов.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:GPA_execution_time_2.png&amp;diff=30335</id>
		<title>Файл:GPA execution time 2.png</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:GPA_execution_time_2.png&amp;diff=30335"/>
		<updated>2025-12-10T03:42:16Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;На графике изображена зависимость времени выполнения трех итераций алгоритма (нулевой и двух последующих) от размера входных данных и числа процессов (график 2)&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:GPA_Execution_time_1.png&amp;diff=30334</id>
		<title>Файл:GPA Execution time 1.png</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:GPA_Execution_time_1.png&amp;diff=30334"/>
		<updated>2025-12-10T03:41:44Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;На графике изображена зависимость времени выполнения трех итераций алгоритма (нулевой и двух последующих) от размера входных данных и числа процессов&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30333</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30333"/>
		<updated>2025-12-09T21:26:44Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Исследование масштабируемости параллельной реализации алгоритма проводилось на суперкомпьютере «Ломоносов-2». Для этого была исследована авторская реализация алгоритма с использованием средств MPI и OpenMP. В ходе исследования было произведено множество запусков программы с различными значениями параметра, задающими размерность форм и их число, а также различным числом процессоров.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Fig2gpa.png|700px|center|мини]] &lt;br /&gt;
[[Файл:Fig1.png|700px|center|мини]]&lt;br /&gt;
&lt;br /&gt;
Можно заметить, что время выполнения программы растет нелинейно.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30329</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30329"/>
		<updated>2025-12-09T21:00:03Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
[[Файл:Fig2gpa.png|700px|мини]] &lt;br /&gt;
[[Файл:Fig1.png|700px|мини]]&lt;br /&gt;
&lt;br /&gt;
Можно заметить, что время выполнения программы растет нелинейно.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30328</id>
		<title>Участник:OscarFox/Обобщенный прокрустов анализ</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7&amp;diff=30328"/>
		<updated>2025-12-09T20:54:02Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Автор&#039;&#039;&#039;: Фостенко О.А.&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Обобщенный прокрустов анализ (Generalized Procrustes Analysis, GPA)&#039;&#039;&#039; - это метод, предназначенный для сравнения и выравнивания нескольких наборов из одного и того же числа точек (форм) в пространстве одной размерности. Цель GPA - найти оптимальные преобразования, включающие вращения, масштабирование и сдвиги, для наилучшего согласования форм друг с другом.&lt;br /&gt;
&lt;br /&gt;
Формулировка похожей задачи встречается в работе B. F. Green 1952 года&amp;lt;ref&amp;gt;&#039;&#039;Green B. F.&#039;&#039; The orthogonal approximation of an oblique structure in factor analysis // Psychometrika. — 1952. — Vol. 17, no. 4. — P. 429–440. [https://doi.org/10.1007/BF02288918]&amp;lt;/ref&amp;gt;, аналитическое решение задачи выравнивания двух множеств точек друг относительно друга с учетом только вращений (ортогональная задача Прокруста) было предложено P. H. Schönemann в 1966 году&amp;lt;ref name=&amp;quot;subalgo&amp;quot;&amp;gt;&#039;&#039;Schönemann P. H.&#039;&#039; A Generalized Solution of the Orthogonal Procrustes Problem // Psychometrika. — 1966. — Vol. 31, no. 1. — P. 1–10. [https://doi.org/10.1007/BF02289451]&amp;lt;/ref&amp;gt;, в 1970 году им же в соавторстве с R. M. Carroll&amp;lt;ref&amp;gt;&#039;&#039;Schönemann P. H., Carroll R. M.&#039;&#039; Fitting one matrix to another under choice of a central dilation and a rigid motion // Psychometrika. — 1970. — Vol. 35, no. 2. — P. 245–255. [https://doi.org/10.1007/BF02291266]&amp;lt;/ref&amp;gt; предложено аналитическое решение для модификации, учитывающей, помимо вращений, смещения и масштабирование (классический прокрустов анализ), а непосредственно алгоритм GPA был разработан и опубликован J. C. Gower в 1975 году&amp;lt;ref name=&amp;quot;Gower&amp;quot;&amp;gt;&#039;&#039;Gower J. C.&#039;&#039; Generalized procrustes analysis // Psychometrika. — 1975. — Vol. 40, no. 1. — P. 33–51. [https://doi.org/10.1007/BF02291478]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GPA - это итеративный алгоритм: он последовательно согласовывает все формы относительно некоторой «средней формы», обновляя её до тех пор, пока изменения не станут незначительными. К «средней форме» набор из точек приводится с помощью классического прокрустова анализа. Иллюстрация выравнивания, выполняемого с помощью как классического, так и обобщенного прокрустова анализа, приведена на рисунке 1.&lt;br /&gt;
&lt;br /&gt;
[[File:Procrustes 2.png|thumb|1000px|center|Рис. 1. Иллюстрация прокрустова анализа на примере форм, отвечающих трехмерным человеческим позам]]&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Описание данных и формул ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\{X_1, X_2,\dots,X_m\}&amp;lt;/math&amp;gt; - набор входных форм, то есть матриц одного размера: &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;. Каждая матрица &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; представляет собой набор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;-мерном пространстве. Точки с одинаковыми индексами (т.е. строки матриц с одинаковыми номерами) предполагаются соответствующими друг другу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные:&#039;&#039;&lt;br /&gt;
* Выровненные формы &amp;lt;math&amp;gt;\{ X_1&#039;, X_2&#039;,\dots,X_m&#039;\}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X_i&#039;\in \mathbb{R}^{n \times d}, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Средняя форма (консенсус) &amp;lt;math&amp;gt;Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Опционально, коэффициенты масштаба &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Вспомогательные понятия:&#039;&#039;&lt;br /&gt;
* &amp;lt;math&amp;gt;CPA(X, Y): \mathbb{R}^{n \times d} \times \mathbb{R}^{n \times d} \rightarrow \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt; - функция, выполняющая классический прокрустов анализ, вращая, смещая и масштабируя форму &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; так, чтобы она наилучшим образом подходила под форму &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Возвращает матрицу из &amp;lt;math&amp;gt;\mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;, отвечающую преобразованной форме &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\| X \|_F = \sqrt{tr(X X^T)}&amp;lt;/math&amp;gt; - норма Фробениуса матрицы &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание алгоритма (GPA)&amp;lt;ref name=&amp;quot;Gower&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;X_i&#039; = s_i X_i R_i + \mathbf{1}_n t_i&amp;lt;/math&amp;gt;. Здесь &amp;lt;math&amp;gt;s_i\in \mathbb{R}&amp;lt;/math&amp;gt; есть константа масштаба, &amp;lt;math&amp;gt;R_i\in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt; - ортогональная матрица, отвечающая вращению, &amp;lt;math&amp;gt;\mathbf{1}_n\in \mathbb{R}^{n \times 1}&amp;lt;/math&amp;gt; - вектор-столбец из единиц, а &amp;lt;math&amp;gt;t_i\in \mathbb{R}^{1 \times d}&amp;lt;/math&amp;gt; - вектор переноса.&lt;br /&gt;
&lt;br /&gt;
В процессе работы GPA итеративно минимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S = \displaystyle \sum_{i&amp;lt;j}^{m} \| X_i&#039; - X_j&#039; \|_F^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
по всевозможным смещениям &amp;lt;math&amp;gt;t_i&amp;lt;/math&amp;gt;, вращениям &amp;lt;math&amp;gt;R_i&amp;lt;/math&amp;gt; и константам масштаба &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt; для всех &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt;, с условием, что &amp;lt;math&amp;gt;\displaystyle \sum_{i=1}^{m} s_i^2 \| X_i \|_F^2 = \sum_{i=1}^{m} \| X_i \|_F^2 &amp;lt;/math&amp;gt; (чтобы избежать зануления всех &amp;lt;math&amp;gt;s_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Минимизация &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; сводится к минимизации &amp;lt;math&amp;gt;S_r = \displaystyle \sum_{i=1}^{m} tr(X_i&#039; X_i&#039;^T) - m \cdot tr(Y Y^T) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - средняя форма (консенсус).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм (GPA)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 1.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация.&lt;br /&gt;
&lt;br /&gt;
Задаем начальные значения выравниваемых форм, центрируя их: &lt;br /&gt;
&lt;br /&gt;
Вычисляем смещение для центрирования форм:  &amp;lt;math&amp;gt;\bar{X_i} = \frac{1}{n} \mathbf{1}_n^T X_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Центрируем формы: &amp;lt;math&amp;gt;X_i&#039; := X_i - \mathbf{1}_n \bar{X_i}, \; i = 1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее нормируем масштаб форм (в совокупности):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\displaystyle X_i&#039; := \lambda X_i&#039;, \; \lambda = \sqrt{\frac{m}{ \sum_{i=1}^{m} \| X_i&#039; \|_F^2}}, \; i=1,\dots,m.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Инициализируем &amp;lt;math&amp;gt;s_i := \lambda, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Задаем начальную среднюю форму, выбирая одну из центрированных форм входного набора (например, первую): &amp;lt;math&amp;gt;Y := X_1&#039; &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 2.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Инициализация функции ошибки.&lt;br /&gt;
&lt;br /&gt;
Выполнить подгонку каждой формы (без учета первой - так как выбрали ее за изначальный консенсус) &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; и оценить функцию ошибки:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall i=2,\dots,m \; X_i&#039; := CPA(X_i&#039;, Y)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Пересчитать консенсус: &amp;lt;math&amp;gt;\displaystyle Y := \frac{1}{m} \sum_{i=1}^{m} X_i&#039; &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
посчитать начальное значение функции потерь: &amp;lt;math&amp;gt;S_r := m(1 - tr(Y Y^T))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что на данном шаге в рамках классического прокрустова анализа (&amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;) вычисляется только матрица поворота, но не смещение и масштаб (так как формы уже центрированы, а масштаб вычисляется далее).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 3.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Выравнивание форм относительно средней.&lt;br /&gt;
&lt;br /&gt;
Для всех текущих форм &amp;lt;math&amp;gt;X_i&#039;, \; i = 1,\dots,m &amp;lt;/math&amp;gt; выполняется их выравнивание относительно средней формы (консенсуса). Заметим, что здесь также выполняется только поворот, но не смещение и масштабирование:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt; присваивается новое значение: &amp;lt;math&amp;gt;X_i&#039; := CPA(X_i&#039;, Y) = X_i&#039; R_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выполняется вычисление нового консенсуса: &amp;lt;math&amp;gt;Y^* := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
также выполняется пересчет функции ошибки: &amp;lt;math&amp;gt;S_r^* := S_r - m \cdot tr(Y^* (Y^*)^T - Y Y^T) &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
и устанавливаем &amp;lt;math&amp;gt;S_r^{**} := S_r^*&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 4.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если масштабирование не требуется, перейти на шаг 6, присвоив &amp;lt;math&amp;gt;Y := Y^*&amp;lt;/math&amp;gt;, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 5.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Масштабирование.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt;i=1,\dots,m&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
выполняется подсчет &amp;lt;math&amp;gt;s_i^* := s_i \cdot \sqrt{ \frac{tr(X_i&#039; Y^T)}{tr(X_i&#039; X_i&#039;^T) \cdot tr(Y Y^T)}}&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
далее присваиваем &amp;lt;math&amp;gt;X_i&#039; := \frac{s_i^*}{s_i} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
устанавливаем &amp;lt;math&amp;gt;s_i := s_i^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
вычисляем новый консенсус: &amp;lt;math&amp;gt;Y^{**} := \frac{1}{m} \sum_{i=1}^{m} X_i&#039;&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
пересчитываем функцию ошибки &amp;lt;math&amp;gt;S_r^{**} := S_r^* - m \cdot tr(Y^{**}(Y^{**})^T - Y^* (Y^*)^T) = S_r -  m \cdot tr(Y^{**}(Y^{**})^T - YY^T)&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
присваиваем &amp;lt;math&amp;gt;Y := Y^{**}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 6.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Проверка критерия останова.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;S_r - S_r^{**} &amp;gt; \varepsilon&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - установленный заранее параметр точности, то установить &amp;lt;math&amp;gt;S_r := S_r^{**}&amp;lt;/math&amp;gt; и перейти на шаг 3, иначе перейти на следующий шаг.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;&amp;lt;b&amp;gt;Шаг 7.&amp;lt;/u&amp;gt;&amp;lt;/b&amp;gt; Завершение итераций.&lt;br /&gt;
&lt;br /&gt;
Вернуть результат: &amp;lt;math&amp;gt;Y, \; X_i&#039;&amp;lt;/math&amp;gt;, а также опционально &amp;lt;math&amp;gt;s_i, \; i=1,\dots,m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Описание подзадачи (CPA)&amp;lt;ref name=&amp;quot;subalgo&amp;quot; /&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Задача классического прокрустова анализа (Classical Procrustes Analysis) без масштабирования и сдвигов заключается в нахождении матрицы вращения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;, минимизирующей &amp;lt;math&amp;gt;\| AR - B \|^2_F&amp;lt;/math&amp;gt; для некоторых матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; одного размера. Такая постановка также называется ортогональной задачей Прокруста (Orthogonal Procrustes Problem, OPA).&lt;br /&gt;
&lt;br /&gt;
У этой задачи есть аналитическое решение. Такая оптимальная матрица поворота находится следующим образом:&lt;br /&gt;
&lt;br /&gt;
Сначала вычисляется произведение &amp;lt;math&amp;gt;M = A^T B&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее для нахождения &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; требуется выполнить сингулярное разложение &amp;lt;math&amp;gt;M: M = U \Sigma V^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда искомая матрица поворота &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; выражается как &amp;lt;math&amp;gt;R = VU^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;det(R) &amp;lt; 0&amp;lt;/math&amp;gt;, то преобразование выполняется с отражением. Чтобы исключить такие случаи, достаточно домножить последний столбец матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;-1&amp;lt;/math&amp;gt;. Тогда для полученного &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; будет выполнено ограничение  &amp;lt;math&amp;gt;det(R)=1&amp;lt;/math&amp;gt; при сохранении оптимальности.&lt;br /&gt;
&lt;br /&gt;
==== Практические аспекты ====&lt;br /&gt;
В данном разделе алгоритм GPA был описан в общем виде, с учетом как его разновидности с масштабированием, так и без него. Важно заметить, что при реализации GPA с масштабированием на практике можно не пересчитывать консенсус и не вычислять функцию ошибки на шаге 3 (далее они все равно будут пересчитаны на шаге 5). Именно такой - оптимизированный - вариант алгоритма GPA с масштабированием будет описан далее.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро каждой итерации GPA зависит от размерности выравниваемых матриц &amp;lt;math&amp;gt;X_i \in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;d &amp;gt; n&amp;lt;/math&amp;gt;, то основную сложность представляют сингулярное разложение и конструирование матрицы вращений в рамках классического прокрустова анализа для каждой формы: &amp;lt;math&amp;gt;CPA(X_i&#039;, Y) = X_i&#039; R_i&amp;lt;/math&amp;gt; (последовательная сложность - &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt;). Иначе, основные вычисления приходятся на матричные умножения видов &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;X, Y \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;. Эти матричные умножения имеют место как в рамках классического прокрустова преобразования, так и при обновлении форм (последовательная сложность - &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В данном разделе используются те же обозначения, что и в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Математическое описание алгоритма&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
В качестве основных макроопераций можно выделить:&lt;br /&gt;
&lt;br /&gt;
* Операция, сочетающая вычисление матрицы вращения с помощью классического прокрустова анализа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление формы через умножение на полученную матрицу: на каждой итерации GPA (после нулевой) выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; таких операций (где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - число форм во входных данных). На нулевой же итерации производится &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; операций этого типа. Сложность операции для каждой формы - &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;, где за вклад в сложность порядка &amp;lt;math&amp;gt;O(d^3)&amp;lt;/math&amp;gt; отвечают сингулярное разложение в &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и вычисление матрицы поворота, а за сложность &amp;lt;math&amp;gt;O(nd^2)&amp;lt;/math&amp;gt; отвечают матричные умножения в рамках алгоритма (см. раздел [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%8F%D0%B4%D1%80%D0%BE_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Вычислительное ядро алгоритма&amp;quot;]).&lt;br /&gt;
* Вычисление консенсусной формы (сложение матриц, отвечающих &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; формам, и умножение этой суммы на скаляр): 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Масштабирование форм (вычисление скаляров для каждой формы): &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций на 1 итерацию GPA, если используется вариант GPA с учетом масштаба (иначе таких операций нет) (на нулевой итерации скаляры принимаются равными одной и той же константе). Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Пересчет функции ошибки: 1 операция на 1 итерацию GPA. Сложность - &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; (требуется вычисление нормы консенсуса).&lt;br /&gt;
&lt;br /&gt;
А также макрооперации для нулевой итерации в частности:&lt;br /&gt;
* Вычисление первоначальной нормировочной константы: последовательная сложность &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Первоначальные нормировка и центровка текущих форм: сложность &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; для нормировки каждой формы.&lt;br /&gt;
* Инициализация показателей масштаба: сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; для каждого при известной константе нормировки.&lt;br /&gt;
&lt;br /&gt;
Представляется разумным отдельно описать макрооперации более низкого уровня, составляющие классический прокрустов анализ &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; и обновление форм &amp;lt;math&amp;gt;X_i&#039;&amp;lt;/math&amp;gt;:&lt;br /&gt;
* Сингулярное разложение матрицы из пространства &amp;lt;math&amp;gt;\mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;X^TY&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X, Y\in \mathbb{R}^{n \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;XR&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; X \in \mathbb{R}^{n \times d}, R \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Перемножение матриц вида &amp;lt;math&amp;gt;UV^T&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; U, V \in \mathbb{R}^{d \times d}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Приведем схему реализации с использованием псевдокода.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации процедуры классического прокрустова преобразования&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function CPA(X, Y, scale=True, translation=True):&lt;br /&gt;
    # Compute column-wise mean if translation is allowed&lt;br /&gt;
    if translation:&lt;br /&gt;
        X_mean = mean_columns(X)&lt;br /&gt;
        Y_mean = mean_columns(Y)&lt;br /&gt;
        X0 = X - X_mean&lt;br /&gt;
        Y0 = Y - Y_mean&lt;br /&gt;
    else:&lt;br /&gt;
        X0 = X&lt;br /&gt;
        Y0 = Y&lt;br /&gt;
        X_mean = 0&lt;br /&gt;
        Y_mean = 0&lt;br /&gt;
&lt;br /&gt;
    # Compute rotation using SVD&lt;br /&gt;
    U, Σ, Vt = SVD(transpose(X0) * Y0)&lt;br /&gt;
    R = U * Vt&lt;br /&gt;
    if determinant(R) &amp;lt; 0:&lt;br /&gt;
        Vt[last_row] *= -1&lt;br /&gt;
        R = U * Vt&lt;br /&gt;
&lt;br /&gt;
    # Compute scale if needed&lt;br /&gt;
    s = 1&lt;br /&gt;
    if scale:&lt;br /&gt;
        s = sum_elements(elementwise_multiply(X0 * R, Y0)) / Frobenius_norm_squared(X0)&lt;br /&gt;
&lt;br /&gt;
    # Align X&lt;br /&gt;
    aligned_X = s * X0 * R + Y_mean&lt;br /&gt;
    if translation:&lt;br /&gt;
        translation_vec = Y_mean - s * X_mean * R&lt;br /&gt;
    else:&lt;br /&gt;
        translation_vec = 0&lt;br /&gt;
&lt;br /&gt;
    return aligned_X, R, s, translation_vec&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Схема реализации GPA&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
function GPA(X_list, scale=True, tolerance=1e-4):&lt;br /&gt;
    m = length(X_list)&lt;br /&gt;
&lt;br /&gt;
    # Center matrices and normalize&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] = X_list[i] - mean_columns(X_list[i])&lt;br /&gt;
&lt;br /&gt;
    var_lambda = sqrt(m / sum(Frobenius_norm_squared(X) for X in X_list))&lt;br /&gt;
    for i in 0..m-1:&lt;br /&gt;
        X_list[i] *= var_lambda&lt;br /&gt;
&lt;br /&gt;
    scaling_factors = [var_lambda for i in 0..m-1]&lt;br /&gt;
&lt;br /&gt;
    # Initial consensus&lt;br /&gt;
    Y_consensus = X_list[0]&lt;br /&gt;
    for i in 1..m-1:&lt;br /&gt;
        X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
    Y_consensus = average(X_list)&lt;br /&gt;
    Sr = m * (1 - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
    # Iterative alignment&lt;br /&gt;
    loop:&lt;br /&gt;
        for i in 0..m-1:&lt;br /&gt;
            X_list[i] = CPA(X_list[i], Y_consensus, scale=False, translation=False)[0]&lt;br /&gt;
&lt;br /&gt;
        if scale:&lt;br /&gt;
            for i in 0..m-1:&lt;br /&gt;
                change_factor = sqrt(sum_elements(elementwise_multiply(X_list[i], Y_consensus))) /&lt;br /&gt;
                                     (Frobenius_norm_squared(X_list[i]) * Frobenius_norm_squared(Y_consensus)))&lt;br /&gt;
                s_i_star = scaling_factors[i] * change_factor&lt;br /&gt;
                X_list[i] *= s_i_star / scaling_factors[i]&lt;br /&gt;
                scaling_factors[i] = s_i_star&lt;br /&gt;
&lt;br /&gt;
        Y_consensus_new = average(X_list)&lt;br /&gt;
        Sr_new = Sr - m * (Frobenius_norm_squared(Y_consensus_new) - Frobenius_norm_squared(Y_consensus))&lt;br /&gt;
&lt;br /&gt;
        if Sr - Sr_new &amp;lt; tolerance:&lt;br /&gt;
            break&lt;br /&gt;
&lt;br /&gt;
        Y_consensus = Y_consensus_new&lt;br /&gt;
        Sr = Sr_new&lt;br /&gt;
&lt;br /&gt;
    return Y_consensus, X_list, scaling_factors&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Как было отмечено ранее в разделе [https://algowiki-project.org/ru/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:OscarFox/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%BA%D1%80%D1%83%D1%81%D1%82%D0%BE%D0%B2_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7#%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0 &amp;quot;Макроструктура алгоритма&amp;quot;], на каждой итерации GPA выполняется &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m-1&amp;lt;/math&amp;gt; на нулевой итерации) операций &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt;, причем на них приходится основная сложность. Сложность каждой из операций типа &amp;lt;math&amp;gt;CPA(X, Y)&amp;lt;/math&amp;gt; равна &amp;lt;math&amp;gt;O(d^3 + nd^2)&amp;lt;/math&amp;gt;. Так как на одной итерации GPA выполняется последовательное приведение сразу &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; (или &amp;lt;math&amp;gt;m - 1&amp;lt;/math&amp;gt; на нулевой итерации) форм к консенсусу с помощью классического прокрустова преобразования, то последовательная сложность одной итерации GPA целиком есть &amp;lt;math&amp;gt;O(m(d^3 + nd^2))&amp;lt;/math&amp;gt;. Число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; заранее неизвестно, так как алгоритм итеративный. Однако, с учетом этого числа сложность всего GPA составит &amp;lt;math&amp;gt;O(km(d^3 + nd^2))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сложность по памяти составит:&lt;br /&gt;
* &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; для хранения входных и текущих матриц&lt;br /&gt;
* &amp;lt;math&amp;gt;O(nd + d^2)&amp;lt;/math&amp;gt; для хранения матрицы поворота для одной формы, временных матриц и векторов, и учитывая сингулярное разложение&lt;br /&gt;
Так как любые временные матрицы размера &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; хранятся лишь на протяжении обработки одной из форм, а формы обрабатываются последовательно, то суммарная сложность последовательного GPA по памяти равна &amp;lt;math&amp;gt;O(mnd + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
GPA имеет свойство быстро сходиться (всего за несколько итераций), особенно при достаточно большом значении допустимой ошибки &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Поэтому для наглядности далее приведен как граф для нулевой итерации, так и граф для последующих итераций GPA. Также отдельно рассматривается информационный граф для процедуры классического прокрустова анализа без сдвигов и масштабирования - именно такой вариант используется в процедуре GPA.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы начальной и последующих итераций GPA (Рис.2 и Рис.3):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;ICtr&#039;&#039;&#039;) отвечает первоначальной центровке форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;λ&#039;&#039;&#039;) отвечает вычислению константы нормализации на начальной итерации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;s_init&#039;&#039;&#039;) отвечает инициализации показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm&#039;&#039;&#039;) отвечает нормировке форм с учетом вычисленной константы нормализации.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Пятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;CPA&#039;&#039;&#039;) отвечает процедуре классического прокрустова преобразования.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шестая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Y&#039;&#039;&#039;) отвечает вычислению консенсуса на основе текущих форм.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Седьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Loss&#039;&#039;&#039;) отвечает пересчету функции ошибки.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Восьмая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Norm_s&#039;&#039;&#039;) отвечает макрооперации, включающей пересчет показателей масштаба, нормировку текущих форм на них и запоминание новых показателей масштаба.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Девятая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;Halt&#039;&#039;&#039;) отвечает проверке критерия останова и возврату из процедуры GPA при его выполнении.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info gpa init.png|thumb|left|600px|Рис. 2. Информационный граф начальной итерации GPA]]&lt;br /&gt;
[[Файл:Info gpa gen.png|thumb|right|790px|Рис. 3. Информационный граф последующих итераций GPA]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационный граф процедуры CPA (Рис.4):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_dnd&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times n&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;SVD&#039;&#039;&#039;) отвечает сингулярному разложению матрицы размерности &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;MatMul_ddd_detchk&#039;&#039;&#039;) отвечает перемножению двух матриц размерностей &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; каждая и коррекции результата в случае наличия отражений.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Четвертая&#039;&#039;&#039; группа вершин (&#039;&#039;&#039;align&#039;&#039;&#039;) отвечает приведению выравниваемой формы, то есть умножению двух матриц размерностей &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d \times d&amp;lt;/math&amp;gt; соответственно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Info cpa.png|thumb|center|250px|Рис. 4. Информационный граф процедуры CPA]]&lt;br /&gt;
&lt;br /&gt;
Также для наглядности в рассмотренные графы добавлены следующие вершины:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_X&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая одной текущей форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_L&#039;&#039;&#039; - вход, вещественное число, отвечающее значению функции ошибки;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_Y&#039;&#039;&#039; - вход, матрица размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, отвечающая консенсусной форме;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;in_s&#039;&#039;&#039; - вход, вещественное число, отвечающее одному показателю масштаба;&lt;br /&gt;
&lt;br /&gt;
Аналогичным образом помечены выходы.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Заметим, что параллелизм возможен только внутри итераций GPA. Итерации выполняются одна за другой последовательно, используя результаты предыдущих.&lt;br /&gt;
&lt;br /&gt;
Сложность SVD заслуживает отдельного внимания, так как есть различные алгоритмы и на практике&lt;br /&gt;
&lt;br /&gt;
Сначала проанализируем параллельную сложность CPA. Отметим, что матричные умножения и высчитывание определителя матрицы поворота в CPA вносят вклад &amp;lt;math&amp;gt;O(\log d + \log n + d) = O(\log n + d)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(d^2 + nd)&amp;lt;/math&amp;gt; в ширину. Если принять сложность SVD (по высоте ЯПФ) за &amp;lt;math&amp;gt;O(d^2)&amp;lt;/math&amp;gt;, тогда суммарный вклад CPA в высоту ЯПФ - &amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;. Таким образом, SVD является определяющим фактором высоты ЯПФ в процедуре CPA, а матричные умножения определяют ширину.&lt;br /&gt;
&lt;br /&gt;
Далее, рассмотрим нулевую итерацию GPA. Центровка каждой формы отдельно вносит вклад &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(nd)&amp;lt;/math&amp;gt; в ширину. Итого, будучи распараллеленной, центровка дает вклад &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину (высота не меняется при учете параллельных вычислений для различных форм независимо).&lt;br /&gt;
Вычисление изначального показателя нормировки занимает &amp;lt;math&amp;gt;O(\log(mnd))&amp;lt;/math&amp;gt; по высоте ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине. Далее нормировка производится за &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ. Вклад инициализации показателей масштаба пренебрежимо мал относительно упомянутых операций. Далее на нулевой итерации выполняется приведение форм с помощью CPA. Для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм параллельное приведение их к консенсусу дает вклад &amp;lt;math&amp;gt;O(m(d^2 + nd))&amp;lt;/math&amp;gt; по ширине ЯПФ, а по высоте - столько же, сколько вносит 1 отдельная процедура CPA (&amp;lt;math&amp;gt;O(\log n + d^2)&amp;lt;/math&amp;gt;). Вычисление консенсуса происходит за &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; шагов при одновременном выполнении &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; операций. Вычисление функции потерь не увеличивает высоту и ширину ЯПФ. Итого, ширина ЯПФ для нулевой итерации GPA составляет &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(\log(mnd) + d^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для последующих итераций GPA также имеет место выполнение &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; процедур CPA одновременно (параллельно), что вносит такой же вклад в сложность, как и выполнение данных процедур на нулевой итерации. Далее, вычисление показателей масштаба происходит за &amp;lt;math&amp;gt;O(\log(nd))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; по ширине ЯПФ (параллельно для &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; форм). Выполняемое далее обновление форм не вносит дополнительной сложности (по асимптотике). Далее, как и на нулевой итерации, происходит вычисление консенуса и пересчет функции ошибки, что вносит вклад &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd)&amp;lt;/math&amp;gt; в ширину. Проверка критерия останова также несущественна по сложности. Итого, последующие итерации вносят вклад &amp;lt;math&amp;gt;O(\log(nd) + \log(m) + d^2) = O(\log(mnd) + d^2)&amp;lt;/math&amp;gt; в высоту ЯПФ и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; в ширину. &lt;br /&gt;
&lt;br /&gt;
Итого, если число итераций GPA равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; (также считаем, что оно не является константой и может неограниченно расти), то можно ожидать сложность в терминах ЯПФ следующего порядка: &amp;lt;math&amp;gt;O(k(\log(mnd) + d^2))&amp;lt;/math&amp;gt; по высоте и &amp;lt;math&amp;gt;O(mnd + md^2)&amp;lt;/math&amp;gt; по ширине. Высота ЯПФ в значительной степени определяется параллельной сложностью (высотой ЯПФ) сингулярного разложения. Также можно отметить компромисс между высотой и шириной ЯПФ, на который может влиять степень  распараллеливания вычисления сумм.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;(X_1, X_2,\dots,X_m)&amp;lt;/math&amp;gt; - набор входных форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;;&lt;br /&gt;
* флаг, отвечающий за включение опции вычисления факторов масштаба (булева переменная);&lt;br /&gt;
* &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; - параметр точности.&lt;br /&gt;
&lt;br /&gt;
Объем входных данных: &amp;lt;math&amp;gt;mnd + 2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Выходные данные: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; - форма консенсуса (матрица размерности &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;);&lt;br /&gt;
* &amp;lt;math&amp;gt;(X&#039;_1, X&#039;_2,\dots,X&#039;_m)&amp;lt;/math&amp;gt; - набор текущих форм, то есть вещественных матриц одного размера &amp;lt;math&amp;gt;n \times d&amp;lt;/math&amp;gt;, выровненных относительно консенсуса;&lt;br /&gt;
* &amp;lt;math&amp;gt;(s_1, s_2,\dots,s_m)&amp;lt;/math&amp;gt; - факторы масштаба (вещественные числа).&lt;br /&gt;
&lt;br /&gt;
Объем выходных данных: &amp;lt;math&amp;gt;mnd + nd + m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Важно заметить, что на практике в задачах, решаемых с помощью этого алгоритма, исследователи часто имеют дело с облаками точек в пространствах размерности 2 (например, в специальных задачах NLP и задачах CV в медицине) или 3 (также в задачах CV). Поэтому в применении к приложениям может быть важна скорее эффективная реализация перемножения матриц, нежели тщательная оптимизация SVD (действительно, в пространствах размерности 2 и 3 в процедуре CPA будет производиться разложение лишь матриц размерностей &amp;lt;math&amp;gt;2 \times 2&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;3 \times 3&amp;lt;/math&amp;gt; соответственно. Также в таком случае решающее значение в оценке сложности алгоритма будет иметь исключительно то, насколько распараллеливаются матричные умножение и сложение.&lt;br /&gt;
&lt;br /&gt;
Обычно число точек &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; сильно больше числа форм &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и размерности пространства &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Также, как было упомянуто ранее, число итераций &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; обычно мало по сравнению с остальными параметрами.&lt;br /&gt;
&lt;br /&gt;
Если принять &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; константами, то сложность последовательной реализации есть &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;, в то время как теоретическая сложность (высота ЯПФ) параллельной реализации будет &amp;lt;math&amp;gt;O(log(mn))&amp;lt;/math&amp;gt;. Очевиден выигрыш по времени.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;k, d&amp;lt;/math&amp;gt; фиксированы, то процедура GPA выполняет константное число операций (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;) на единицу входных  (и выходных) данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
[[Файл:Fig2gpa.png|700px|мини]] &lt;br /&gt;
[[Файл:Fig1.png|700px|мини]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Fig1.png&amp;diff=30327</id>
		<title>Файл:Fig1.png</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Fig1.png&amp;diff=30327"/>
		<updated>2025-12-09T20:53:38Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;График зависимости времени от параметров (placeholder file 2)&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Fig2gpa.png&amp;diff=30326</id>
		<title>Файл:Fig2gpa.png</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Fig2gpa.png&amp;diff=30326"/>
		<updated>2025-12-09T20:52:31Z</updated>

		<summary type="html">&lt;p&gt;OscarFox: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;График зависимости времени от параметров (placeholder file)&lt;/div&gt;</summary>
		<author><name>OscarFox</name></author>
	</entry>
</feed>