# Модуль `differential_eqs`

Модуль `differential_eqs` предоставляет набор функций для численного решения обыкновенных дифференциальных уравнений (ОДУ) и систем ОДУ, а также для визуализации их решений и фазовых портретов.  В основе лежат методы Эйлера (явный метод первого порядка), который может быть использован для моделирования различных динамических систем.

## Функции модуля

### 1. `euler_method`

Решает ОДУ вида `dy/dx = f(x, y)` численным методом Эйлера.

**Теоретическое описание:**

Метод Эйлера — это численный метод первого порядка для аппроксимации решения ОДУ. Он основан на использовании касательной к решению в начальной точке для экстраполяции значения решения в следующей точке.  Формула метода:

```
y_{n+1} = y_n + h * f(x_n, y_n)
```

где:

- `y_{n+1}` — приближенное значение решения в точке `x_{n+1}`
- `y_n` — значение решения в точке `x_n`
- `h` — шаг интегрирования (разница между `x_{n+1}` и `x_n`)
- `f(x_n, y_n)` — значение функции, определяющей правую часть ОДУ, в точке `(x_n, y_n)`

Метод Эйлера является явным, так как для вычисления `y_{n+1}` используются только значения в предыдущей точке `(x_n, y_n)`.  Он имеет первый порядок точности, что означает, что ошибка на каждом шаге пропорциональна `h²`, а глобальная ошибка (накопленная ошибка на интервале) пропорциональна `h`.

**Практическая реализация:**

Функция `euler_method` принимает функцию `f`, начальные условия `x0` и `y0`, шаг `h` и конечное значение `xn`.  Она итеративно вычисляет значения `y` для последовательных значений `x` от `x0` до `xn` с шагом `h`, используя формулу метода Эйлера.  Результатом является кортеж из двух списков: значений `x` и соответствующих значений `y`.

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

- `f` (callable): Функция, определяющая ОДУ `dy/dx = f(x, y)`.  Принимает два аргумента: `x` и `y`.
- `x0` (float): Начальное значение `x`.
- `y0` (float): Начальное значение `y`.
- `h` (float): Шаг интегрирования.
- `xn` (float): Конечное значение `x`.

**Возвращаемое значение:**

- `tuple[list[float], list[float]]`: Кортеж, содержащий список значений `x` и список соответствующих значений `y`.

**Пример:**

```python
import matplotlib.pyplot as plt
import numpy as np

# Решение dy/dx = y, y(0) = 1 на интервале [0, 2] с h=0.1
def f(x, y):
    return y

x_values, y_values = euler_method(f, x0=0, y0=1, h=0.1, xn=2)

plt.plot(x_values, y_values, label="Метод Эйлера")
plt.plot(x_values, np.exp(x_values), label="Точное решение")
plt.legend()
plt.show()
```

**Замечания:**

- Метод Эйлера прост в реализации, но его точность ограничена, особенно при больших значениях `h` или для уравнений с быстро меняющимися решениями.
- Для повышения точности можно использовать методы более высокого порядка, такие как методы Рунге-Кутты.

### 2. `plot_euler_solutions`

Строит график решений ОДУ методом Эйлера для разных шагов интегрирования и сравнивает их с аналитическим решением.

**Теоретическое описание:**

Функция использует метод Эйлера для получения численных решений ОДУ с разными шагами интегрирования.  Затем она строит графики этих решений вместе с графиком аналитического (точного) решения, что позволяет визуально оценить влияние шага интегрирования на точность численного решения.

**Практическая реализация:**

Функция `plot_euler_solutions` принимает функцию `f`, определяющую ОДУ, функцию `exact_solution`, возвращающую аналитическое решение, начальные условия `x0` и `y0`, конечное значение `xn` и список шагов интегрирования `h_list`.  Для каждого шага из `h_list` вычисляется численное решение методом Эйлера, и строится его график.  Также строится график аналитического решения.  Результатом работы функции является список кортежей, каждый из которых содержит значения `x` и `y` для численного решения с соответствующим шагом.

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

- `f` (callable): Функция, определяющая ОДУ `dy/dx = f(x, y)`.
- `exact_solution` (callable): Функция, возвращающая аналитическое решение ОДУ в зависимости от `x`.
- `x0` (float): Начальное значение `x`.
- `y0` (float): Начальное значение `y`.
- `xn` (float): Конечное значение `x`.
- `h_list` (list[float]): Список шагов интегрирования.

**Возвращаемое значение:**

- `list[tuple[list[float], list[float]]]`: Список кортежей, содержащих значения `x` и `y` для численных решений с разными шагами.

**Пример:**

```python
import numpy as np
import matplotlib.pyplot as plt

# Решение dy/dx = y, y(0) = 1 на интервале [0, 2] с шагами [0.1, 0.05]
def f(x, y):
    return y

def exact(x):
    return np.exp(x)

h_list = [0.1, 0.05]
solutions = plot_euler_solutions(f, exact, x0=0, y0=1, xn=2, h_list=h_list)

print("Количество решений:", len(solutions))
```

**Замечания:**

- Графики, построенные этой функцией, позволяют наглядно оценить сходимость численного решения к аналитическому при уменьшении шага интегрирования.
- Выбор оптимального шага интегрирования зависит от конкретной задачи и требуемой точности.

### 3. `euler_system`

Решает систему ОДУ численным методом Эйлера.

**Теоретическое описание:**

Метод Эйлера может быть обобщен для решения систем ОДУ.  Рассмотрим систему:

```
dy_i/dt = f_i(t, y_1, y_2, ..., y_n),  i = 1, 2, ..., n
```

где `y_i` — зависимые переменные, `t` — независимая переменная (обычно время), а `f_i` — функции, определяющие правые части уравнений.  Метод Эйлера для этой системы выглядит следующим образом:

```
y_i^{(k+1)} = y_i^{(k)} + h * f_i(t_k, y_1^{(k)}, y_2^{(k)}, ..., y_n^{(k)}),  i = 1, 2, ..., n
```

где:

- `y_i^{(k+1)}` — значение переменной `y_i` на следующем шаге по времени
- `y_i^{(k)}` — значение переменной `y_i` на текущем шаге
- `h` — шаг по времени
- `t_k` — текущее время
- `f_i` — функция, определяющая производную `y_i`

**Практическая реализация:**

Функция `euler_system` принимает список функций `funcs`, определяющих правые части уравнений, начальные условия `initial_conditions`, начальное время `t0`, конечное время `tn` и шаг по времени `h`.  Она итеративно вычисляет значения всех переменных системы для последовательных моментов времени от `t0` до `tn` с шагом `h`, используя формулу метода Эйлера для систем.  Результатом является кортеж, содержащий список значений времени и список списков, где каждый внутренний список содержит значения всех переменных в соответствующий момент времени.

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

- `funcs` (list[callable]): Список функций, определяющих правые части уравнений системы.  Каждая функция принимает текущее время `t` и список текущих значений переменных и возвращает значение производной соответствующей переменной.
- `initial_conditions` (list[float]): Список начальных значений переменных.
- `t0` (float): Начальное время.
- `tn` (float): Конечное время.
- `h` (float): Шаг по времени.

**Возвращаемое значение:**

- `tuple[list[float], list[list[float]]]`: Кортеж, содержащий список значений времени и список списков значений переменных.

**Пример:**

```python
# Решение системы dx/dt = -y, dy/dt = x на интервале [0, 2π] с h=0.1
import numpy as np

# Функции системы
def f1(t, vars):
    x, y = vars
    return -y

def f2(t, vars):
    x, y = vars
    return x

t_vals, [x_vals, y_vals] = euler_system(
    funcs=[f1, f2],
    initial_conditions=[1, 0],
    t0=0.0,
    tn=2 * np.pi,
    h=0.1
)

# Вывод результатов для нескольких точек
for t, x, y in zip(t_vals[::10], x_vals[::10], y_vals[::10]):
    print(f"t = {t:.1f}, x = {x:.2f}, y = {y:.2f}")
```

**Замечания:**

- При решении систем ОДУ методом Эйлера важно учитывать, что ошибка может накапливаться для всех переменных.
- Для систем с жесткими уравнениями (уравнения, имеющие решения с разными масштабами времени) метод Эйлера может быть неустойчивым.  В таких случаях рекомендуется использовать неявные методы или методы с адаптивным шагом.

### 4. `plot_phase_portrait_with_streamplot`

Строит фазовый портрет системы ОДУ с визуализацией векторного поля через `streamplot` и траекторий, вычисленных методом Эйлера.

**Теоретическое описание:**

Фазовый портрет — это графическое представление решений системы ОДУ на плоскости, где осями являются зависимые переменные.  Для системы двух ОДУ:

```
dx/dt = f(x, y)
dy/dt = g(x, y)
```

фазовый портрет строится в плоскости `(x, y)`.  В каждой точке этой плоскости можно вычислить вектор `(f(x, y), g(x, y))`, который показывает направление и скорость изменения решения в этой точке.  Совокупность этих векторов образует векторное поле.  Линии тока (streamlines) этого поля представляют собой траектории решений системы.

**Практическая реализация:**

Функция `plot_phase_portrait_with_streamplot` принимает список функций `funcs` (в данном случае две функции, определяющие `dx/dt` и `dy/dt`), диапазоны значений переменных `x_range` и `y_range`, начальное и конечное время `t0` и `tn`, шаг `h`, плотность сетки для векторного поля `grid_density`, список начальных условий для траекторий `trajectories` и опции для настройки графиков `stream_options` и `euler_options`.  Она вычисляет векторное поле на сетке, строит его линии тока с помощью `matplotlib.pyplot.streamplot`, а также вычисляет и строит траектории решений для заданных начальных условий методом Эйлера.

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

- `funcs` (list[callable]): Список из двух функций, определяющих `dx/dt` и `dy/dt`.
- `x_range` (tuple[float, float]): Диапазон значений `x`.
- `y_range` (tuple[float, float]): Диапазон значений `y`.
- `t0` (float): Начальное время.
- `tn` (float): Конечное время.
- `h` (float): Шаг по времени.
- `grid_density` (int): Плотность сетки для векторного поля.
- `trajectories` (list[list[float]] or None): Список начальных условий для траекторий.
- `stream_options` (dict or None): Опции для настройки `streamplot`.
- `euler_options` (dict or None): Опции для настройки траекторий Эйлера.

**Возвращаемое значение:**

- `None`: Функция строит график и не возвращает значение.

**Пример:**

```python
# Построение фазового портрета системы хищник-жертва
a, b, c = 1.0, 0.1, 0.02
d, e = 0.5, 0.01

def f1(t, vars):
    x, y = vars
    return a * x - b * x**2 - c * x * y

def f2(t, vars):
    x, y = vars
    return -d * y + e * x * y

plot_phase_portrait_with_streamplot(
    funcs=[f1, f2],
    x_range=(0, 50),
    y_range=(0, 50),
    t0=0.0,
    tn=100.0,
    h=0.1,
    trajectories=[[40, 20], [10, 5]],
    stream_options={'color': 'gray', 'density': 1.2},
    euler_options={'color': 'blue', 'lw': 1.5}
)
```

**Замечания:**

- Фазовый портрет дает наглядное представление о качественном поведении решений системы ОДУ, например, о наличии устойчивых и неустойчивых состояний равновесия, предельных циклов и т.д.
- Параметры `stream_options` и `euler_options` позволяют настроить внешний вид графика, например, цвет и толщину линий, плотность линий тока и т.д.

### 5. `plot_phase_portrait_3d`

Строит фазовый портрет трехмерной системы ОДУ с визуализацией векторных полей в проекциях и траектории в 3D-пространстве.

**Теоретическое описание:**

Для системы трех ОДУ:

```
dx/dt = f(x, y, z)
dy/dt = g(x, y, z)
dz/dt = h(x, y, z)
```

фазовый портрет можно визуализировать в трехмерном пространстве `(x, y, z)`.  Однако, для наглядности часто строят проекции на плоскости `(x, y)`, `(x, z)` и `(y, z)`.  В каждой проекции можно визуализировать векторное поле, аналогично двумерному случаю.  Также можно построить трехмерную траекторию решения в пространстве `(x, y, z)`.

**Практическая реализация:**

Функция `plot_phase_portrait_3d` принимает список функций `funcs` (три функции, определяющие `dx/dt`, `dy/dt` и `dz/dt`), начальные условия `initial_conditions`, начальное и конечное время `t0` и `tn`, шаг `h`, диапазоны значений переменных `x_range`, `y_range` и `z_range`, плотность сетки `grid_density` и опции для настройки графиков `stream_options` и `euler_options`.  Она вычисляет траекторию решения методом Эйлера и строит ее в 3D-пространстве.  Также она строит векторные поля в проекциях на плоскости, используя `matplotlib.pyplot.streamplot`.  Векторные поля вычисляются при фиксированном значении третьей переменной (например, для проекции `(x, y)` используется начальное значение `z`).

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

- `funcs` (list[callable]): Список из трех функций, определяющих `dx/dt`, `dy/dt` и `dz/dt`.
- `initial_conditions` (list[float]): Начальные значения `x`, `y` и `z`.
- `t0` (float): Начальное время.
- `tn` (float): Конечное время.
- `h` (float): Шаг по времени.
- `x_range` (tuple[float, float]): Диапазон значений `x`.
- `y_range` (tuple[float, float]): Диапазон значений `y`.
- `z_range` (tuple[float, float]): Диапазон значений `z`.
- `grid_density` (int): Плотность сетки.
- `stream_options` (dict or None): Опции для `streamplot`.
- `euler_options` (dict or None): Опции для траектории Эйлера.

**Возвращаемое значение:**

- `None`: Функция строит графики и не возвращает значение.

**Пример:**

```python
# Построение фазового портрета системы Лоренца
sigma, r, b = 10, 28, 8/3

def f_x(t, vars):
    return sigma * (vars[1] - vars[0])

def f_y(t, vars):
    return vars[0] * (r - vars[2]) - vars[1]

def f_z(t, vars):
    return vars[0] * vars[1] - b * vars[2]

plot_phase_portrait_3d(
    funcs=[f_x, f_y, f_z],
    initial_conditions=[10, 10, 10],
    t0=0.0,
    tn=30.0,
    h=0.01,
    grid_density=10
)
```

**Замечания:**

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

### 6. `euler_delay_system`

Решает систему ОДУ с задержкой методом Эйлера.

**Теоретическое описание:**

Системы ОДУ с задержкой — это системы, в которых производные переменных зависят не только от текущих значений переменных, но и от их значений в прошлом.  Рассмотрим систему с одной задержкой `τ`:

```
dy_i/dt = f_i(t, y_1(t), ..., y_n(t), y_1(t-τ), ..., y_n(t-τ)),  i = 1, ..., n
```

Для решения таких систем методом Эйлера необходимо хранить историю значений переменных и использовать интерполяцию для вычисления значений в моменты времени `t-τ`.  В простейшем случае можно использовать линейную интерполяцию.

**Практическая реализация:**

Функция `euler_delay_system` решает систему из четырех ОДУ с одной задержкой `τ`.  Уравнения системы жестко заданы внутри функции и описывают модель с четырьмя переменными: `V`, `C`, `F` и `m`.  Функция принимает начальные условия, параметры модели (включая значение задержки `τ`), конечное время `t_end` и шаг `h`.  Она использует метод Эйлера с линейной интерполяцией для вычисления значений переменных с задержкой.  Для хранения истории значений используется структура данных `deque` (двусторонняя очередь), которая позволяет эффективно добавлять и удалять элементы с обоих концов.  Это позволяет поддерживать буфер истории ограниченного размера, храня только необходимые значения для вычисления задержки.

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

- `funcs` (list[callable]):  **Не используется в текущей реализации.**  Предназначен для передачи функций, вычисляющих производные, но уравнения жестко зашиты.
- `initial_conditions` (list[float]): Начальные значения `V`, `C`, `F` и `m`.
- `params` (dict): Словарь с параметрами модели (см. пример).
- `t_end` (float): Конечное время.
- `h` (float): Шаг по времени.

**Возвращаемое значение:**

- `tuple[list[float], list[float], list[float], list[float], list[float]]`: Кортеж, содержащий список значений времени и списки значений `V`, `C`, `F` и `m`.

**Пример:**

```python
# Решение системы с задержкой
params = {
    'beta': 1.0, 'gamma': 0.8, 'alpha': 100,
    'mu_C': 0.9, 'C_star': 1, 'rho': 0.01,
    'mu_f': 0.2, 'eta': 0.01, 'sigma': 0.12,
    'mu_m': 1.0, 'tau': 0.5, 't0': 0.0
}
initial_conditions = [0.5, 0.5, 0.2, 0.0]

t_values, V, C, F, m = euler_delay_system(
    funcs=[],  # Пустой список, так как функции не используются
    initial_conditions=initial_conditions,
    params=params,
    t_end=10,
    h=0.01
)

print("Первые 5 значений V:", V[:5])
print("Первые 5 значений C:", C[:5])
```

**Замечания:**

- Уравнения системы жестко заданы внутри функции, что ограничивает ее применение.  Для решения других систем с задержкой потребуется модификация кода функции.
- Использование буфера истории позволяет эффективно работать с задержками, но размер буфера должен быть достаточным для хранения значений на интервале задержки.
- Метод Эйлера для систем с задержкой также имеет первый порядок точности, и для повышения точности можно использовать методы более высокого порядка.

### 7. `plot_phase_portrait_4d`

Строит фазовые портреты для четырехмерной системы ОДУ с задержкой через 2D проекции.

**Теоретическое описание:**

Визуализация фазовых портретов для систем с более чем двумя переменными затруднена.  Для четырехмерных систем (как в случае системы с задержкой, решаемой функцией `euler_delay_system`) можно использовать проекции на плоскости, образованные парами переменных.  Например, можно построить фазовый портрет в проекции `(V, C)`, где по оси абсцисс откладываются значения `V`, а по оси ординат — значения `C`.  Для каждой проекции можно визуализировать векторное поле и траектории решений.

**Практическая реализация:**

Функция `plot_phase_portrait_4d` принимает список функций `funcs` (определяющих производные переменных, но в текущей реализации не используются, так как уравнения жестко заданы в `euler_delay_system`), начальные условия, параметры модели, конечное время, шаг, список пар переменных для проекций, плотность сетки и опции для настройки графиков.  Она решает систему с задержкой с помощью `euler_delay_system`, а затем строит фазовые портреты для заданных пар переменных.  Для каждой пары вычисляется векторное поле при фиксированных значениях двух других переменных (используются начальные значения).  Векторное поле визуализируется с помощью `matplotlib.pyplot.streamplot`, а траектория решения строится поверх него.

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

- `funcs` (list[callable]): **Не используется в текущей реализации.**
- `initial_conditions` (list[float]): Начальные значения `V`, `C`, `F` и `m`.
- `params` (dict): Параметры модели.
- `t_end` (float): Конечное время.
- `h` (float): Шаг.
- `pairs` (list[tuple[str, str]] or None): Список пар переменных для проекций.  Если `None`, используются пары `('V', 'C')`, `('V', 'F')`, `('C', 'F')` и `('V', 'm')`.
- `grid_density` (int): Плотность сетки.
- `stream_options` (dict or None): Опции для `streamplot`.
- `euler_options` (dict or None): Опции для траекторий.
- `var_ranges` (dict or None): Диапазоны значений переменных.
- `axis_limits` (dict or None): Границы осей для конкретных проекций.

**Возвращаемое значение:**

- `tuple[list[float], list[float], list[float], list[float], list[float]]`: Кортеж со значениями времени и переменных.

**Пример:**

```python
# Построение фазового портрета для системы с задержкой
params = {
    'beta': 1.0, 'gamma': 0.8, 'alpha': 100,
    'mu_C': 0.9, 'C_star': 1, 'rho': 0.01,
    'mu_f': 0.2, 'eta': 0.01, 'sigma': 0.12,
    'mu_m': 1.0, 'tau': 0.5, 't0': 0.0
}
initial_conditions = [0.5, 0.5, 0.2, 0.0]

t_vals, V_vals, C_vals, F_vals, m_vals = plot_phase_portrait_4d(
    funcs=[],
    initial_conditions=initial_conditions,
    params=params,
    t_end=25.0,
    h=0.001,
    pairs=[('V', 'C'), ('V', 'F')],
    grid_density=50
)
```

**Замечания:**

- Выбор пар переменных для проекций зависит от целей анализа.  Разные проекции могут выявлять разные аспекты поведения системы.
- Фиксирование значений двух переменных при вычислении векторного поля является упрощением, но позволяет визуализировать динамику в выбранной проекции.
- Для более полного анализа можно рассмотреть несколько значений фиксированных переменных или использовать другие методы визуализации многомерных данных.

## Общие замечания

- Во всех функциях используется метод Эйлера, который является методом первого порядка.  Это означает, что ошибка на каждом шаге пропорциональна шагу интегрирования, а глобальная ошибка (накопленная на интервале) также пропорциональна шагу.  Для повышения точности можно использовать методы более высокого порядка, такие как методы Рунге-Кутты.
- При решении систем ОДУ важно учитывать, что ошибка может накапливаться для всех переменных.
- Для систем с жесткими уравнениями метод Эйлера может быть неустойчивым.  В таких случаях рекомендуется использовать неявные методы или методы с адаптивным шагом.
- При работе с системами ОДУ с задержкой необходимо хранить историю значений переменных и использовать интерполяцию для вычисления значений в моменты времени с задержкой.
- Визуализация фазовых портретов является мощным инструментом для анализа качественного поведения динамических систем.  Для систем с более чем двумя переменными используются проекции на плоскости.
