# Модуль `jacobi` для вычисления собственных значений и векторов методом Якоби

Этот модуль содержит реализации метода Якоби для вычисления собственных значений и собственных векторов симметричных матриц. Метод Якоби является итерационным алгоритмом, который последовательно обнуляет внедиагональные элементы матрицы с помощью ортогональных преобразований (вращений). В результате матрица сходится к диагональной, где на диагонали расположены собственные значения, а столбцы произведения матриц вращений образуют собственные векторы.

## Теоретическая справка

### Метод Якоби

Метод Якоби основан на следующей идее:

1. **Диагонализация матрицы:** Цель состоит в том, чтобы привести симметричную матрицу $A$ к диагональному виду $D$ с помощью последовательных ортогональных преобразований:

   $$
   D = V^T A V
   $$

   где $V$ - ортогональная матрица, столбцы которой являются собственными векторами $A$, а $D$ - диагональная матрица с собственными значениями на диагонали.
2. **Вращения Якоби:** На каждом шаге алгоритма выбирается пара внедиагональных элементов $a_{ij}$ и $a_{ji}$ (с максимальным абсолютным значением), и строится матрица вращения $J(i, j, \theta)$, такая что при умножении $A$ на $J$ слева и на $J^T$ справа, элементы $a_{ij}$ и $a_{ji}$ обнуляются. Матрица вращения имеет вид:

   $$
   J(i, j, \theta) =
   \begin{bmatrix}
   1 & \cdots & 0 & \cdots & 0 & \cdots & 0 \\
   \vdots & \ddots & \vdots & & \vdots & & \vdots \\
   0 & \cdots & \cos\theta & \cdots & -\sin\theta & \cdots & 0 \\
   \vdots & & \vdots & \ddots & \vdots & & \vdots \\
   0 & \cdots & \sin\theta & \cdots & \cos\theta & \cdots & 0 \\
   \vdots & & \vdots & & \vdots & \ddots & \vdots \\
   0 & \cdots & 0 & \cdots & 0 & \cdots & 1
   \end{bmatrix}
   $$

   где $\cos\theta$ и $\sin\theta$ стоят на пересечениях строк и столбцов $i$ и $j$. Угол $\theta$ выбирается таким образом, чтобы обнулить элементы $a_{ij}$ и $a_{ji}$ в преобразованной матрице.
3. **Вычисление угла поворота:** Угол $\theta$ вычисляется по формуле:

   $$
   \tan 2\theta = \frac{2a_{ij}}{a_{ii} - a_{jj}}
   $$

   Для повышения устойчивости вычислений используется следующая параметризация:

   $$
   \alpha = \frac{a_{jj} - a_{ii}}{2a_{ij}}
   $$

   $$
   t = \frac{\operatorname{sign}(\alpha)}{|\alpha| + \sqrt{1 + \alpha^2}}
   $$

   $$
   c = \frac{1}{\sqrt{1 + t^2}}
   $$

   $$
   s = tc
   $$

   где $c = \cos\theta$ и $s = \sin\theta$.
4. **Итерационный процесс:** Описанные выше шаги повторяются до тех пор, пока все внедиагональные элементы не станут достаточно малыми (меньше заданной точности `tol`).

### Реализации в модуле

Модуль `jacobi` предоставляет три реализации метода Якоби:

1. `jacobi_eigen`: Базовая реализация с использованием NumPy для векторизованных операций.
2. `numba_jacobi_eigen`: Реализация с использованием Numba для JIT-компиляции, что повышает производительность за счет компиляции Python-кода в машинный код.
3. `numba_jacobi_eigen_parallel`: Параллельная реализация с использованием Numba для распараллеливания вычислений на многоядерных процессорах, что обеспечивает максимальную производительность.

## Описание функций

### `jacobi_eigen(A, tol=1e-10, max_iter=20000)`

Вычисляет собственные значения и собственные векторы симметричной матрицы `A` методом Якоби.

**Параметры:**

- `A` : `np.ndarray`, форма `(n, n)`
  - Входная симметричная матрица.
- `tol` : `float`, необязательный, по умолчанию `1e-10`
  - Порог точности для завершения итераций.
- `max_iter` : `int`, необязательный, по умолчанию `20000`
  - Максимальное количество итераций.

**Возвращает:**

- `eigenvalues` : `np.ndarray`, форма `(n,)`
  - Массив собственных значений.
- `V` : `np.ndarray`, форма `(n, n)`
  - Матрица собственных векторов.

**Пример:**

```python
import numpy as np
from matplobblib.nm.eigen.jacobi import jacobi_eigen

A = np.array([[4, 1], [1, 3]], dtype=np.float64)
eigenvalues, eigenvectors = jacobi_eigen(A)
print("Собственные значения:", eigenvalues)
print("Собственные векторы:\n", eigenvectors)
```

### `numba_jacobi_eigen(A, tol=1e-10, max_iter=20000)`

Вычисляет собственные значения и собственные векторы симметричной матрицы `A` методом Якоби с использованием Numba для ускорения.

**Параметры:**

- `A` : `np.ndarray`, форма `(n, n)`
  - Входная симметричная матрица.
- `tol` : `float`, необязательный, по умолчанию `1e-10`
  - Порог точности для завершения итераций.
- `max_iter` : `int`, необязательный, по умолчанию `20000`
  - Максимальное количество итераций.

**Возвращает:**

- `eigenvalues` : `np.ndarray`, форма `(n,)`
  - Массив собственных значений.
- `V` : `np.ndarray`, форма `(n, n)`
  - Матрица собственных векторов.

**Пример:**

```python
import numpy as np
from matplobblib.nm.eigen.jacobi import numba_jacobi_eigen

A = np.array([[4, 1], [1, 3]], dtype=np.float64)
eigenvalues, eigenvectors = numba_jacobi_eigen(A)
print("Собственные значения:", eigenvalues)
print("Собственные векторы:\n", eigenvectors)
```

### `numba_jacobi_eigen_parallel(A, tol=1e-10, max_iter=20000)`

Вычисляет собственные значения и собственные векторы симметричной матрицы `A` методом Якоби с использованием Numba и параллельных вычислений.

**Параметры:**

- `A` : `np.ndarray`, форма `(n, n)`
  - Входная симметричная матрица.
- `tol` : `float`, необязательный, по умолчанию `1e-10`
  - Порог точности для завершения итераций.
- `max_iter` : `int`, необязательный, по умолчанию `20000`
  - Максимальное количество итераций.

**Возвращает:**

- `eigenvalues` : `np.ndarray`, форма `(n,)`
  - Массив собственных значений.
- `V` : `np.ndarray`, форма `(n, n)`
  - Матрица собственных векторов.

**Пример:**

```python
import numpy as np
from matplobblib.nm.eigen.jacobi import numba_jacobi_eigen_parallel

A = np.array([[4, 1], [1, 3]], dtype=np.float64)
eigenvalues, eigenvectors = numba_jacobi_eigen_parallel(A)
print("Собственные значения:", eigenvalues)
print("Собственные векторы:\n", eigenvectors)
```

## Замечания

- Все функции предполагают, что входная матрица `A` является симметричной. Использование несимметричных матриц может привести к некорректным результатам.
- Параллельная реализация `numba_jacobi_eigen_parallel` обеспечивает наилучшую производительность на многоядерных системах, но может иметь небольшие отклонения в точности из-за использования `fastmath=True`.
- Для достижения максимальной производительности рекомендуется использовать `numba_jacobi_eigen_parallel` для больших матриц.
- Базовая реализация `jacobi_eigen` может быть полезна для отладки и понимания алгоритма, но она менее эффективна по сравнению с реализациями на Numba.

## Ссылки

- Golub, G. H., & Van Loan, C. F. (2013). Matrix computations. Johns Hopkins University Press.
- Numba documentation: https://numba.readthedocs.io/
- Parallel computing with Numba: https://numba.pydata.org/numba-doc/latest/user/parallel.html
- "Making Python extremely fast with Numba: Advanced Deep Dive (1/3)": https://towardsdatascience.com/making-python-extremely-fast-with-numba-advanced-deep-dive-1-of-3-8cb13ed4f5b9
