Compare commits
No commits in common. "claude" and "symbolic" have entirely different histories.
255
main.py
255
main.py
|
|
@ -4,6 +4,7 @@ from typing import Callable, List, Tuple, Optional, Dict, Any, Union
|
||||||
import dataclasses
|
import dataclasses
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
import inspect
|
import inspect
|
||||||
|
from visualizer import *
|
||||||
|
|
||||||
# ====================== ТИПЫ ЗАВИСИМОСТЕЙ ======================
|
# ====================== ТИПЫ ЗАВИСИМОСТЕЙ ======================
|
||||||
|
|
||||||
|
|
@ -292,265 +293,27 @@ class MathematicalModel:
|
||||||
|
|
||||||
return self.main_formula(**call_args)
|
return self.main_formula(**call_args)
|
||||||
|
|
||||||
# ====================== УЛУЧШЕННЫЙ ВИЗУАЛИЗАТОР ======================
|
|
||||||
|
|
||||||
class ModelVisualizer:
|
|
||||||
"""Класс для визуализации модели с зависимостями"""
|
|
||||||
|
|
||||||
def __init__(self, model: MathematicalModel):
|
|
||||||
self.model = model
|
|
||||||
self.figures = {}
|
|
||||||
|
|
||||||
def plot_parameter(self, param_name: str,
|
|
||||||
x_range: Optional[Tuple[float, float]] = None,
|
|
||||||
figsize: Tuple[int, int] = (10, 6),
|
|
||||||
title: Optional[str] = None,
|
|
||||||
show_dependencies: bool = True) -> plt.Figure:
|
|
||||||
"""
|
|
||||||
Строит график для параметра с учетом его зависимостей
|
|
||||||
"""
|
|
||||||
if param_name not in self.model.parameters:
|
|
||||||
raise ValueError(f"Параметр '{param_name}' не найден")
|
|
||||||
|
|
||||||
param = self.model.parameters[param_name]
|
|
||||||
|
|
||||||
# Определяем диапазон x
|
|
||||||
if x_range is None:
|
|
||||||
if isinstance(param, IndependentParameter):
|
|
||||||
x_range = param.x_range
|
|
||||||
else:
|
|
||||||
# Для зависимых параметров нужно определить разумный диапазон
|
|
||||||
x_range = (0, 10) # По умолчанию
|
|
||||||
|
|
||||||
x = np.linspace(x_range[0], x_range[1], 1000)
|
|
||||||
|
|
||||||
# Вычисляем значение параметра
|
|
||||||
context = self.model.evaluate_parameters(x)
|
|
||||||
y = context[param_name]
|
|
||||||
|
|
||||||
# Создаем фигуру
|
|
||||||
fig, ax = plt.subplots(figsize=figsize)
|
|
||||||
|
|
||||||
# Строим основной график
|
|
||||||
ax.plot(x, y, color=param.color, linestyle=param.line_style,
|
|
||||||
linewidth=2, label=param_name)
|
|
||||||
|
|
||||||
# Если нужно показать зависимости
|
|
||||||
if show_dependencies and isinstance(param, DependentParameter):
|
|
||||||
for dep_name in param.dependencies:
|
|
||||||
if dep_name in context:
|
|
||||||
dep_y = context[dep_name]
|
|
||||||
# Нормализуем для отображения на том же графике
|
|
||||||
dep_y_normalized = (dep_y - np.min(dep_y)) / (np.max(dep_y) - np.min(dep_y) + 1e-10)
|
|
||||||
ax.plot(x, dep_y_normalized, '--', alpha=0.5,
|
|
||||||
label=f"{dep_name} (норм.)")
|
|
||||||
|
|
||||||
ax.grid(True, alpha=0.3)
|
|
||||||
ax.set_title(title or f"График параметра: {param_name}\n{param.description}", fontsize=14)
|
|
||||||
ax.set_xlabel("x", fontsize=12)
|
|
||||||
ax.set_ylabel("Значение", fontsize=12)
|
|
||||||
ax.legend(loc='best')
|
|
||||||
|
|
||||||
self.figures[f"param_{param_name}"] = fig
|
|
||||||
return fig
|
|
||||||
|
|
||||||
def plot_parameter_with_dependencies(self, param_name: str,
|
|
||||||
figsize: Tuple[int, int] = (14, 10)) -> plt.Figure:
|
|
||||||
"""
|
|
||||||
Строит подробный график параметра и всех его зависимостей
|
|
||||||
"""
|
|
||||||
if param_name not in self.model.parameters:
|
|
||||||
raise ValueError(f"Параметр '{param_name}' не найден")
|
|
||||||
|
|
||||||
param = self.model.parameters[param_name]
|
|
||||||
|
|
||||||
# Собираем все зависимости (рекурсивно)
|
|
||||||
def get_all_deps(name, deps_set):
|
|
||||||
deps = self.model.get_parameter_dependencies(name)
|
|
||||||
for dep in deps:
|
|
||||||
if dep not in deps_set:
|
|
||||||
deps_set.add(dep)
|
|
||||||
get_all_deps(dep, deps_set)
|
|
||||||
return deps_set
|
|
||||||
|
|
||||||
all_deps = list(get_all_deps(param_name, set()))
|
|
||||||
|
|
||||||
# Определяем диапазон x
|
|
||||||
x_range = (0, 10)
|
|
||||||
if isinstance(param, IndependentParameter):
|
|
||||||
x_range = param.x_range
|
|
||||||
elif all_deps:
|
|
||||||
# Пытаемся найти независимый параметр среди зависимостей
|
|
||||||
for dep in all_deps:
|
|
||||||
p = self.model.parameters[dep]
|
|
||||||
if isinstance(p, IndependentParameter):
|
|
||||||
x_range = p.x_range
|
|
||||||
break
|
|
||||||
|
|
||||||
x = np.linspace(x_range[0], x_range[1], 1000)
|
|
||||||
context = self.model.evaluate_parameters(x)
|
|
||||||
|
|
||||||
# Создаем подграфики
|
|
||||||
n_plots = len(all_deps) + 1
|
|
||||||
cols = min(3, n_plots)
|
|
||||||
rows = (n_plots + cols - 1) // cols
|
|
||||||
|
|
||||||
fig, axes = plt.subplots(rows, cols, figsize=figsize)
|
|
||||||
axes = axes.flatten() if n_plots > 1 else [axes]
|
|
||||||
|
|
||||||
# График целевого параметра
|
|
||||||
axes[0].plot(x, context[param_name], color=param.color,
|
|
||||||
linestyle=param.line_style, linewidth=2.5)
|
|
||||||
axes[0].grid(True, alpha=0.3)
|
|
||||||
axes[0].set_title(f"{param_name} (целевой)", fontsize=12)
|
|
||||||
axes[0].set_xlabel("x")
|
|
||||||
|
|
||||||
# Графики зависимостей
|
|
||||||
for i, dep_name in enumerate(all_deps, 1):
|
|
||||||
if i < len(axes):
|
|
||||||
dep_param = self.model.parameters[dep_name]
|
|
||||||
axes[i].plot(x, context[dep_name], color=dep_param.color,
|
|
||||||
linestyle=dep_param.line_style, linewidth=2)
|
|
||||||
axes[i].grid(True, alpha=0.3)
|
|
||||||
axes[i].set_title(f"{dep_name}\n{getattr(dep_param, 'description', '')}", fontsize=10)
|
|
||||||
axes[i].set_xlabel("x")
|
|
||||||
|
|
||||||
# Скрываем лишние подграфики
|
|
||||||
for j in range(len(all_deps) + 1, len(axes)):
|
|
||||||
axes[j].set_visible(False)
|
|
||||||
|
|
||||||
plt.tight_layout()
|
|
||||||
self.figures[f"param_{param_name}_with_deps"] = fig
|
|
||||||
return fig
|
|
||||||
|
|
||||||
def plot_harmonics(self, t_range: Tuple[float, float] = (0, 10),
|
|
||||||
figsize: Tuple[int, int] = (12, 8),
|
|
||||||
x_for_dependencies: Optional[np.ndarray] = None) -> plt.Figure:
|
|
||||||
"""
|
|
||||||
Строит графики гармоник с учетом возможных зависимостей от параметров
|
|
||||||
"""
|
|
||||||
if not self.model.harmonics:
|
|
||||||
raise ValueError("Нет гармонических колебаний")
|
|
||||||
|
|
||||||
t = np.linspace(t_range[0], t_range[1], 1000)
|
|
||||||
|
|
||||||
# Подготавливаем контекст для зависимых гармоник
|
|
||||||
if x_for_dependencies is not None:
|
|
||||||
context = self.model.evaluate_parameters(x_for_dependencies)
|
|
||||||
else:
|
|
||||||
# Если не задан x, используем значения по умолчанию
|
|
||||||
context = {}
|
|
||||||
for name, param in self.model.parameters.items():
|
|
||||||
if isinstance(param, IndependentParameter):
|
|
||||||
x_default = np.linspace(param.x_range[0], param.x_range[1], 1)
|
|
||||||
context[name] = param.evaluate({'x': x_default})[0]
|
|
||||||
|
|
||||||
n_plots = len(self.model.harmonics) + 1
|
|
||||||
fig, axes = plt.subplots(n_plots, 1, figsize=(figsize[0], figsize[1] * n_plots/3))
|
|
||||||
|
|
||||||
# Индивидуальные гармоники
|
|
||||||
for i, h in enumerate(self.model.harmonics):
|
|
||||||
harmonic_values = h.evaluate(t, context)
|
|
||||||
axes[i].plot(t, harmonic_values, linewidth=1.5)
|
|
||||||
axes[i].grid(True, alpha=0.3)
|
|
||||||
|
|
||||||
# Показываем зависимости в заголовке
|
|
||||||
deps = h.get_dependencies()
|
|
||||||
deps_str = f" (зависит от: {', '.join(deps)})" if deps else ""
|
|
||||||
axes[i].set_title(f"Гармоника {i+1}: {h}{deps_str}")
|
|
||||||
axes[i].set_ylabel("Амплитуда")
|
|
||||||
|
|
||||||
# Сумма гармоник
|
|
||||||
total = np.zeros_like(t)
|
|
||||||
for h in self.model.harmonics:
|
|
||||||
total += h.evaluate(t, context)
|
|
||||||
|
|
||||||
axes[-1].plot(t, total, 'r-', linewidth=2)
|
|
||||||
axes[-1].grid(True, alpha=0.3)
|
|
||||||
axes[-1].set_title("Сумма всех гармоник")
|
|
||||||
axes[-1].set_xlabel("Время t")
|
|
||||||
axes[-1].set_ylabel("Амплитуда")
|
|
||||||
|
|
||||||
plt.tight_layout()
|
|
||||||
self.figures["harmonics"] = fig
|
|
||||||
return fig
|
|
||||||
|
|
||||||
def plot_dependency_graph(self, figsize: Tuple[int, int] = (12, 8)) -> plt.Figure:
|
|
||||||
"""
|
|
||||||
Визуализирует граф зависимостей между параметрами
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
import networkx as nx
|
|
||||||
except ImportError:
|
|
||||||
print("Для визуализации графа зависимостей установите networkx: pip install networkx")
|
|
||||||
return None
|
|
||||||
|
|
||||||
G = nx.DiGraph()
|
|
||||||
|
|
||||||
# Добавляем узлы и ребра
|
|
||||||
for name, param in self.model.parameters.items():
|
|
||||||
node_attrs = {
|
|
||||||
'color': 'lightblue' if isinstance(param, IndependentParameter) else 'lightgreen'
|
|
||||||
}
|
|
||||||
G.add_node(name, **node_attrs)
|
|
||||||
|
|
||||||
if isinstance(param, DependentParameter):
|
|
||||||
for dep in param.dependencies:
|
|
||||||
G.add_edge(dep, name)
|
|
||||||
|
|
||||||
# Добавляем гармоники как узлы, если у них есть зависимости
|
|
||||||
for i, h in enumerate(self.model.harmonics):
|
|
||||||
deps = h.get_dependencies()
|
|
||||||
if deps:
|
|
||||||
harmonic_name = f"Harmonic_{i+1}"
|
|
||||||
G.add_node(harmonic_name, color='lightcoral')
|
|
||||||
for dep in deps:
|
|
||||||
G.add_edge(dep, harmonic_name)
|
|
||||||
|
|
||||||
fig, ax = plt.subplots(figsize=figsize)
|
|
||||||
|
|
||||||
# Раскладка графа
|
|
||||||
pos = nx.spring_layout(G, k=2, iterations=50)
|
|
||||||
|
|
||||||
# Рисуем граф
|
|
||||||
node_colors = [G.nodes[node].get('color', 'lightgray') for node in G.nodes]
|
|
||||||
nx.draw(G, pos, with_labels=True, node_color=node_colors,
|
|
||||||
node_size=2000, font_size=10, font_weight='bold',
|
|
||||||
arrows=True, arrowsize=20, ax=ax)
|
|
||||||
|
|
||||||
ax.set_title("Граф зависимостей параметров", fontsize=16)
|
|
||||||
|
|
||||||
self.figures["dependency_graph"] = fig
|
|
||||||
return fig
|
|
||||||
|
|
||||||
# ====================== ПРИМЕР ИСПОЛЬЗОВАНИЯ ======================
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Пример использования с зависимыми параметрами"""
|
"""Пример использования с зависимыми параметрами"""
|
||||||
|
|
||||||
# Создаем модель
|
# Создаем модель
|
||||||
model = MathematicalModel()
|
model = MathematicalModel()
|
||||||
# Константы
|
|
||||||
U0 = 1.0
|
|
||||||
h = 1.0
|
|
||||||
pi = np.pi
|
|
||||||
h_br = h / 2 / pi
|
|
||||||
|
|
||||||
# Добавляем независимые параметры (базовые)
|
# Добавляем независимые параметры (базовые)
|
||||||
model.add_independent_parameter(
|
model.add_independent_parameter(
|
||||||
name="n",
|
name="time",
|
||||||
formula=lambda x: 1.0 * np.ones_like(x), # Константа
|
formula=lambda x: x, # Просто время
|
||||||
x_range=(0, 10),
|
x_range=(0, 10),
|
||||||
color='gray',
|
color='gray',
|
||||||
description="Для дисперсионного уравнения"
|
description="Базовый параметр времени"
|
||||||
)
|
)
|
||||||
|
|
||||||
model.add_independent_parameter(
|
model.add_independent_parameter(
|
||||||
name="x0_br",
|
name="A0",
|
||||||
formula=lambda x: 1.0 * np.ones_like(x), # Константа
|
formula=lambda x: 2.0 * np.ones_like(x), # Константа
|
||||||
x_range=(0, 10),
|
x_range=(0, 10),
|
||||||
color='blue',
|
color='blue',
|
||||||
description="Порог"
|
description="Базовая амплитуда"
|
||||||
)
|
)
|
||||||
|
|
||||||
model.add_independent_parameter(
|
model.add_independent_parameter(
|
||||||
|
|
@ -563,9 +326,9 @@ def main():
|
||||||
|
|
||||||
# Добавляем зависимые параметры (выражаются через другие)
|
# Добавляем зависимые параметры (выражаются через другие)
|
||||||
model.add_dependent_parameter(
|
model.add_dependent_parameter(
|
||||||
name="Kn",
|
name="A_effective",
|
||||||
expression=lambda A0, time: A0 * np.exp(-0.1 * time), # Затухающая амплитуда
|
expression=lambda A0, time: A0 * np.exp(-0.1 * time), # Затухающая амплитуда
|
||||||
dependencies=["n", "x0_br"],
|
dependencies=["A0", "time"],
|
||||||
color='green',
|
color='green',
|
||||||
description="Эффективная амплитуда (зависит от времени)"
|
description="Эффективная амплитуда (зависит от времени)"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
228
visualizer.py
Normal file
228
visualizer.py
Normal file
|
|
@ -0,0 +1,228 @@
|
||||||
|
class ModelVisualizer:
|
||||||
|
"""Класс для визуализации модели с зависимостями"""
|
||||||
|
|
||||||
|
def __init__(self, model: MathematicalModel):
|
||||||
|
self.model = model
|
||||||
|
self.figures = {}
|
||||||
|
|
||||||
|
def plot_parameter(self, param_name: str,
|
||||||
|
x_range: Optional[Tuple[float, float]] = None,
|
||||||
|
figsize: Tuple[int, int] = (10, 6),
|
||||||
|
title: Optional[str] = None,
|
||||||
|
show_dependencies: bool = True) -> plt.Figure:
|
||||||
|
"""
|
||||||
|
Строит график для параметра с учетом его зависимостей
|
||||||
|
"""
|
||||||
|
if param_name not in self.model.parameters:
|
||||||
|
raise ValueError(f"Параметр '{param_name}' не найден")
|
||||||
|
|
||||||
|
param = self.model.parameters[param_name]
|
||||||
|
|
||||||
|
# Определяем диапазон x
|
||||||
|
if x_range is None:
|
||||||
|
if isinstance(param, IndependentParameter):
|
||||||
|
x_range = param.x_range
|
||||||
|
else:
|
||||||
|
# Для зависимых параметров нужно определить разумный диапазон
|
||||||
|
x_range = (0, 10) # По умолчанию
|
||||||
|
|
||||||
|
x = np.linspace(x_range[0], x_range[1], 1000)
|
||||||
|
|
||||||
|
# Вычисляем значение параметра
|
||||||
|
context = self.model.evaluate_parameters(x)
|
||||||
|
y = context[param_name]
|
||||||
|
|
||||||
|
# Создаем фигуру
|
||||||
|
fig, ax = plt.subplots(figsize=figsize)
|
||||||
|
|
||||||
|
# Строим основной график
|
||||||
|
ax.plot(x, y, color=param.color, linestyle=param.line_style,
|
||||||
|
linewidth=2, label=param_name)
|
||||||
|
|
||||||
|
# Если нужно показать зависимости
|
||||||
|
if show_dependencies and isinstance(param, DependentParameter):
|
||||||
|
for dep_name in param.dependencies:
|
||||||
|
if dep_name in context:
|
||||||
|
dep_y = context[dep_name]
|
||||||
|
# Нормализуем для отображения на том же графике
|
||||||
|
dep_y_normalized = (dep_y - np.min(dep_y)) / (np.max(dep_y) - np.min(dep_y) + 1e-10)
|
||||||
|
ax.plot(x, dep_y_normalized, '--', alpha=0.5,
|
||||||
|
label=f"{dep_name} (норм.)")
|
||||||
|
|
||||||
|
ax.grid(True, alpha=0.3)
|
||||||
|
ax.set_title(title or f"График параметра: {param_name}\n{param.description}", fontsize=14)
|
||||||
|
ax.set_xlabel("x", fontsize=12)
|
||||||
|
ax.set_ylabel("Значение", fontsize=12)
|
||||||
|
ax.legend(loc='best')
|
||||||
|
|
||||||
|
self.figures[f"param_{param_name}"] = fig
|
||||||
|
return fig
|
||||||
|
|
||||||
|
def plot_parameter_with_dependencies(self, param_name: str,
|
||||||
|
figsize: Tuple[int, int] = (14, 10)) -> plt.Figure:
|
||||||
|
"""
|
||||||
|
Строит подробный график параметра и всех его зависимостей
|
||||||
|
"""
|
||||||
|
if param_name not in self.model.parameters:
|
||||||
|
raise ValueError(f"Параметр '{param_name}' не найден")
|
||||||
|
|
||||||
|
param = self.model.parameters[param_name]
|
||||||
|
|
||||||
|
# Собираем все зависимости (рекурсивно)
|
||||||
|
def get_all_deps(name, deps_set):
|
||||||
|
deps = self.model.get_parameter_dependencies(name)
|
||||||
|
for dep in deps:
|
||||||
|
if dep not in deps_set:
|
||||||
|
deps_set.add(dep)
|
||||||
|
get_all_deps(dep, deps_set)
|
||||||
|
return deps_set
|
||||||
|
|
||||||
|
all_deps = list(get_all_deps(param_name, set()))
|
||||||
|
|
||||||
|
# Определяем диапазон x
|
||||||
|
x_range = (0, 10)
|
||||||
|
if isinstance(param, IndependentParameter):
|
||||||
|
x_range = param.x_range
|
||||||
|
elif all_deps:
|
||||||
|
# Пытаемся найти независимый параметр среди зависимостей
|
||||||
|
for dep in all_deps:
|
||||||
|
p = self.model.parameters[dep]
|
||||||
|
if isinstance(p, IndependentParameter):
|
||||||
|
x_range = p.x_range
|
||||||
|
break
|
||||||
|
|
||||||
|
x = np.linspace(x_range[0], x_range[1], 1000)
|
||||||
|
context = self.model.evaluate_parameters(x)
|
||||||
|
|
||||||
|
# Создаем подграфики
|
||||||
|
n_plots = len(all_deps) + 1
|
||||||
|
cols = min(3, n_plots)
|
||||||
|
rows = (n_plots + cols - 1) // cols
|
||||||
|
|
||||||
|
fig, axes = plt.subplots(rows, cols, figsize=figsize)
|
||||||
|
axes = axes.flatten() if n_plots > 1 else [axes]
|
||||||
|
|
||||||
|
# График целевого параметра
|
||||||
|
axes[0].plot(x, context[param_name], color=param.color,
|
||||||
|
linestyle=param.line_style, linewidth=2.5)
|
||||||
|
axes[0].grid(True, alpha=0.3)
|
||||||
|
axes[0].set_title(f"{param_name} (целевой)", fontsize=12)
|
||||||
|
axes[0].set_xlabel("x")
|
||||||
|
|
||||||
|
# Графики зависимостей
|
||||||
|
for i, dep_name in enumerate(all_deps, 1):
|
||||||
|
if i < len(axes):
|
||||||
|
dep_param = self.model.parameters[dep_name]
|
||||||
|
axes[i].plot(x, context[dep_name], color=dep_param.color,
|
||||||
|
linestyle=dep_param.line_style, linewidth=2)
|
||||||
|
axes[i].grid(True, alpha=0.3)
|
||||||
|
axes[i].set_title(f"{dep_name}\n{getattr(dep_param, 'description', '')}", fontsize=10)
|
||||||
|
axes[i].set_xlabel("x")
|
||||||
|
|
||||||
|
# Скрываем лишние подграфики
|
||||||
|
for j in range(len(all_deps) + 1, len(axes)):
|
||||||
|
axes[j].set_visible(False)
|
||||||
|
|
||||||
|
plt.tight_layout()
|
||||||
|
self.figures[f"param_{param_name}_with_deps"] = fig
|
||||||
|
return fig
|
||||||
|
|
||||||
|
def plot_harmonics(self, t_range: Tuple[float, float] = (0, 10),
|
||||||
|
figsize: Tuple[int, int] = (12, 8),
|
||||||
|
x_for_dependencies: Optional[np.ndarray] = None) -> plt.Figure:
|
||||||
|
"""
|
||||||
|
Строит графики гармоник с учетом возможных зависимостей от параметров
|
||||||
|
"""
|
||||||
|
if not self.model.harmonics:
|
||||||
|
raise ValueError("Нет гармонических колебаний")
|
||||||
|
|
||||||
|
t = np.linspace(t_range[0], t_range[1], 1000)
|
||||||
|
|
||||||
|
# Подготавливаем контекст для зависимых гармоник
|
||||||
|
if x_for_dependencies is not None:
|
||||||
|
context = self.model.evaluate_parameters(x_for_dependencies)
|
||||||
|
else:
|
||||||
|
# Если не задан x, используем значения по умолчанию
|
||||||
|
context = {}
|
||||||
|
for name, param in self.model.parameters.items():
|
||||||
|
if isinstance(param, IndependentParameter):
|
||||||
|
x_default = np.linspace(param.x_range[0], param.x_range[1], 1)
|
||||||
|
context[name] = param.evaluate({'x': x_default})[0]
|
||||||
|
|
||||||
|
n_plots = len(self.model.harmonics) + 1
|
||||||
|
fig, axes = plt.subplots(n_plots, 1, figsize=(figsize[0], figsize[1] * n_plots/3))
|
||||||
|
|
||||||
|
# Индивидуальные гармоники
|
||||||
|
for i, h in enumerate(self.model.harmonics):
|
||||||
|
harmonic_values = h.evaluate(t, context)
|
||||||
|
axes[i].plot(t, harmonic_values, linewidth=1.5)
|
||||||
|
axes[i].grid(True, alpha=0.3)
|
||||||
|
|
||||||
|
# Показываем зависимости в заголовке
|
||||||
|
deps = h.get_dependencies()
|
||||||
|
deps_str = f" (зависит от: {', '.join(deps)})" if deps else ""
|
||||||
|
axes[i].set_title(f"Гармоника {i+1}: {h}{deps_str}")
|
||||||
|
axes[i].set_ylabel("Амплитуда")
|
||||||
|
|
||||||
|
# Сумма гармоник
|
||||||
|
total = np.zeros_like(t)
|
||||||
|
for h in self.model.harmonics:
|
||||||
|
total += h.evaluate(t, context)
|
||||||
|
|
||||||
|
axes[-1].plot(t, total, 'r-', linewidth=2)
|
||||||
|
axes[-1].grid(True, alpha=0.3)
|
||||||
|
axes[-1].set_title("Сумма всех гармоник")
|
||||||
|
axes[-1].set_xlabel("Время t")
|
||||||
|
axes[-1].set_ylabel("Амплитуда")
|
||||||
|
|
||||||
|
plt.tight_layout()
|
||||||
|
self.figures["harmonics"] = fig
|
||||||
|
return fig
|
||||||
|
|
||||||
|
def plot_dependency_graph(self, figsize: Tuple[int, int] = (12, 8)) -> plt.Figure:
|
||||||
|
"""
|
||||||
|
Визуализирует граф зависимостей между параметрами
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
import networkx as nx
|
||||||
|
except ImportError:
|
||||||
|
print("Для визуализации графа зависимостей установите networkx: pip install networkx")
|
||||||
|
return None
|
||||||
|
|
||||||
|
G = nx.DiGraph()
|
||||||
|
|
||||||
|
# Добавляем узлы и ребра
|
||||||
|
for name, param in self.model.parameters.items():
|
||||||
|
node_attrs = {
|
||||||
|
'color': 'lightblue' if isinstance(param, IndependentParameter) else 'lightgreen'
|
||||||
|
}
|
||||||
|
G.add_node(name, **node_attrs)
|
||||||
|
|
||||||
|
if isinstance(param, DependentParameter):
|
||||||
|
for dep in param.dependencies:
|
||||||
|
G.add_edge(dep, name)
|
||||||
|
|
||||||
|
# Добавляем гармоники как узлы, если у них есть зависимости
|
||||||
|
for i, h in enumerate(self.model.harmonics):
|
||||||
|
deps = h.get_dependencies()
|
||||||
|
if deps:
|
||||||
|
harmonic_name = f"Harmonic_{i+1}"
|
||||||
|
G.add_node(harmonic_name, color='lightcoral')
|
||||||
|
for dep in deps:
|
||||||
|
G.add_edge(dep, harmonic_name)
|
||||||
|
|
||||||
|
fig, ax = plt.subplots(figsize=figsize)
|
||||||
|
|
||||||
|
# Раскладка графа
|
||||||
|
pos = nx.spring_layout(G, k=2, iterations=50)
|
||||||
|
|
||||||
|
# Рисуем граф
|
||||||
|
node_colors = [G.nodes[node].get('color', 'lightgray') for node in G.nodes]
|
||||||
|
nx.draw(G, pos, with_labels=True, node_color=node_colors,
|
||||||
|
node_size=2000, font_size=10, font_weight='bold',
|
||||||
|
arrows=True, arrowsize=20, ax=ax)
|
||||||
|
|
||||||
|
ax.set_title("Граф зависимостей параметров", fontsize=16)
|
||||||
|
|
||||||
|
self.figures["dependency_graph"] = fig
|
||||||
|
return fig
|
||||||
Loading…
Reference in New Issue
Block a user