expression 6 ub

begin
This commit is contained in:
kit8nino 2026-02-18 12:25:14 +03:00
parent ea55212306
commit 40cbdf36c7

826
main.py
View File

@ -1,304 +1,688 @@
import numpy as np import numpy as np
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from typing import Dict, List, Callable, Any, Optional from typing import Callable, List, Tuple, Optional, Dict, Any, Union
import networkx as nx import dataclasses
from functools import lru_cache from enum import Enum
import sympy as sp import inspect
from collections import defaultdict
# ====================== ТИПЫ ЗАВИСИМОСТЕЙ ======================
class DependencyType(Enum):
"""Типы зависимостей между параметрами"""
INDEPENDENT = "independent" # Независимый параметр
DEPENDENT = "dependent" # Зависимый параметр
EXPRESSION = "expression" # Выражение, использующее другие параметры
# ====================== КЛАССЫ ДЛЯ ПАРАМЕТРОВ ======================
@dataclasses.dataclass
class Parameter: class Parameter:
"""Класс для представления параметра с его формулой и зависимостями""" """Базовый класс для параметра"""
name: str
param_type: DependencyType
description: str = ""
def __init__(self, name: str, formula: Callable, dependencies: List[str] = None, def evaluate(self, context: Dict[str, Any]) -> np.ndarray:
description: str = "", units: str = ""): """Вычисляет значение параметра в заданном контексте"""
self.name = name raise NotImplementedError
self.formula = formula # Функция для вычисления параметра
self.dependencies = dependencies or [] # Список зависимых параметров
self.description = description
self.units = units
self.cache = {} # Кэш для мемоизации
def calculate(self, x_values: np.ndarray, param_values: Dict[str, np.ndarray]) -> np.ndarray: @dataclasses.dataclass
"""Вычисление значений параметра для заданных x""" class IndependentParameter(Parameter):
# Проверка кэша """Независимый параметр (базовый)"""
cache_key = (tuple(x_values), tuple(sorted(param_values.items()))) formula: Callable
if cache_key in self.cache: x_range: Tuple[float, float]
return self.cache[cache_key] num_points: int = 1000
color: str = 'blue'
line_style: str = '-'
# Вычисление def __post_init__(self):
if self.dependencies: self.param_type = DependencyType.INDEPENDENT
# Подготовка аргументов для формулы
args = [x_values] + [param_values[dep] for dep in self.dependencies] def evaluate(self, context: Dict[str, Any]) -> np.ndarray:
result = self.formula(*args) """Вычисляет значение независимого параметра"""
x = context.get('x', np.linspace(self.x_range[0], self.x_range[1], self.num_points))
return self.formula(x)
@dataclasses.dataclass
class DependentParameter(Parameter):
"""Зависимый параметр (выражается через другие параметры)"""
expression: Callable # Функция, которая использует другие параметры
dependencies: List[str] # Имена параметров, от которых зависит
color: str = 'green'
line_style: str = '--'
def __post_init__(self):
self.param_type = DependencyType.DEPENDENT
def evaluate(self, context: Dict[str, Any]) -> np.ndarray:
"""Вычисляет значение зависимого параметра"""
# Собираем значения зависимостей из контекста
dep_values = {}
for dep_name in self.dependencies:
if dep_name not in context:
raise ValueError(f"Зависимость '{dep_name}' не найдена в контексте")
dep_values[dep_name] = context[dep_name]
# Вычисляем выражение
return self.expression(**dep_values)
# ====================== КЛАСС ДЛЯ ГАРМОНИК ======================
class HarmonicOscillation:
"""Класс для описания гармонического колебания"""
def __init__(self, amplitude: Union[float, str],
frequency: Union[float, str],
phase: Union[float, str] = 0,
amplitude_depends_on: Optional[List[str]] = None,
frequency_depends_on: Optional[List[str]] = None,
phase_depends_on: Optional[List[str]] = None):
"""
Параметры гармоники могут быть как числами, так и именами параметров модели
"""
self.amplitude = amplitude
self.frequency = frequency
self.phase = phase
# Отслеживаем зависимости
self.amplitude_depends_on = amplitude_depends_on or []
self.frequency_depends_on = frequency_depends_on or []
self.phase_depends_on = phase_depends_on or []
def get_dependencies(self) -> List[str]:
"""Возвращает все зависимости гармоники"""
return (self.amplitude_depends_on +
self.frequency_depends_on +
self.phase_depends_on)
def evaluate(self, t: np.ndarray, context: Dict[str, Any]) -> np.ndarray:
"""Вычисляет значение гармонического колебания с учетом зависимостей"""
# Получаем значения амплитуды, частоты и фазы
amp = self._get_value(self.amplitude, context)
freq = self._get_value(self.frequency, context)
phase = self._get_value(self.phase, context)
return amp * np.sin(2 * np.pi * freq * t + phase)
def _get_value(self, param: Any, context: Dict[str, Any]) -> float:
"""Получает значение параметра (число или из контекста)"""
if isinstance(param, (int, float)):
return param
elif isinstance(param, str) and param in context:
# Если параметр - строка, берем соответствующее значение из контекста
val = context[param]
# Если это массив, берем среднее или первое значение
if isinstance(val, np.ndarray):
return np.mean(val) # или val[0] в зависимости от логики
return float(val)
else: else:
result = self.formula(x_values) raise ValueError(f"Не удалось получить значение для {param}")
# Сохранение в кэш def __repr__(self):
self.cache[cache_key] = result return f"Harmonic(A={self.amplitude}, f={self.frequency}, φ={self.phase})"
return result
def clear_cache(self): # ====================== ОСНОВНОЙ КЛАСС МОДЕЛИ ======================
"""Очистка кэша параметра"""
self.cache.clear()
class MathematicalModel:
class FormulaModel: """
"""Основной класс для управления моделью с параметрами""" Модель с поддержкой зависимых параметров
"""
def __init__(self): def __init__(self):
self.parameters: Dict[str, Parameter] = {} self.parameters: Dict[str, Parameter] = {}
self.harmonics: List[HarmonicOscillation] = []
self.main_formula: Optional[Callable] = None self.main_formula: Optional[Callable] = None
self.dependency_graph = nx.DiGraph() self.evaluation_context: Dict[str, Any] = {}
def add_parameter(self, parameter: Parameter): def add_independent_parameter(self, name: str, formula: Callable,
"""Добавление параметра в модель""" x_range: Tuple[float, float],
self.parameters[parameter.name] = parameter num_points: int = 1000,
color: str = 'blue',
line_style: str = '-',
description: str = "") -> None:
"""Добавляет независимый параметр"""
self.parameters[name] = IndependentParameter(
name=name,
formula=formula,
x_range=x_range,
num_points=num_points,
color=color,
line_style=line_style,
description=description
)
# Обновление графа зависимостей def add_dependent_parameter(self, name: str, expression: Callable,
self.dependency_graph.add_node(parameter.name) dependencies: List[str],
for dep in parameter.dependencies: color: str = 'green',
self.dependency_graph.add_edge(dep, parameter.name) line_style: str = '--',
description: str = "") -> None:
"""Добавляет зависимый параметр"""
# Проверяем, что все зависимости существуют
for dep in dependencies:
if dep not in self.parameters:
raise ValueError(f"Зависимость '{dep}' не найдена среди параметров")
# Проверка на циклические зависимости self.parameters[name] = DependentParameter(
if not nx.is_directed_acyclic_graph(self.dependency_graph): name=name,
raise ValueError(f"Циклическая зависимость обнаружена при добавлении параметра {parameter.name}") expression=expression,
dependencies=dependencies,
color=color,
line_style=line_style,
description=description
)
def set_main_formula(self, formula: Callable, dependencies: List[str]): def add_harmonic(self, amplitude: Union[float, str],
"""Установка основной формулы модели""" frequency: Union[float, str],
phase: Union[float, str] = 0,
amplitude_depends_on: Optional[List[str]] = None,
frequency_depends_on: Optional[List[str]] = None,
phase_depends_on: Optional[List[str]] = None) -> None:
"""Добавляет гармоническое колебание с возможными зависимостями"""
harmonic = HarmonicOscillation(
amplitude=amplitude,
frequency=frequency,
phase=phase,
amplitude_depends_on=amplitude_depends_on,
frequency_depends_on=frequency_depends_on,
phase_depends_on=phase_depends_on
)
# Проверяем, что все зависимости существуют
for dep in harmonic.get_dependencies():
if dep not in self.parameters:
raise ValueError(f"Зависимость '{dep}' для гармоники не найдена среди параметров")
self.harmonics.append(harmonic)
def set_main_formula(self, formula: Callable) -> None:
"""Устанавливает основную формулу"""
self.main_formula = formula self.main_formula = formula
self.main_dependencies = dependencies
def get_calculation_order(self) -> List[str]: def get_parameter_dependencies(self, param_name: str) -> List[str]:
"""Получение порядка вычисления параметров с учетом зависимостей""" """Возвращает список зависимостей для параметра"""
try: param = self.parameters.get(param_name)
return list(nx.topological_sort(self.dependency_graph)) if isinstance(param, DependentParameter):
except nx.NetworkXError: return param.dependencies
raise ValueError("Невозможно определить порядок вычисления из-за циклических зависимостей") return []
def calculate_all_parameters(self, x_values: np.ndarray) -> Dict[str, np.ndarray]: def get_all_dependencies(self) -> Dict[str, List[str]]:
"""Вычисление всех параметров в правильном порядке""" """Возвращает словарь всех зависимостей"""
param_values = {} dependencies = {}
calculation_order = self.get_calculation_order() for name, param in self.parameters.items():
if isinstance(param, DependentParameter):
dependencies[name] = param.dependencies
return dependencies
def evaluate_parameters(self, x: np.ndarray) -> Dict[str, np.ndarray]:
"""
Вычисляет все параметры с учетом зависимостей.
Использует топологическую сортировку для правильного порядка вычисления.
"""
results = {'x': x}
# Функция для топологической сортировки
def topological_sort():
visited = set()
order = []
def dfs(param_name):
if param_name in visited:
return
visited.add(param_name)
for param_name in calculation_order:
if param_name in self.parameters:
param = self.parameters[param_name] param = self.parameters[param_name]
param_values[param_name] = param.calculate(x_values, param_values) if isinstance(param, DependentParameter):
for dep in param.dependencies:
if dep in self.parameters:
dfs(dep)
return param_values order.append(param_name)
def calculate_main_formula(self, x_values: np.ndarray) -> np.ndarray: for name in self.parameters:
"""Вычисление основной формулы""" if name not in visited:
dfs(name)
return order
# Вычисляем параметры в правильном порядке
eval_order = topological_sort()
for param_name in eval_order:
param = self.parameters[param_name]
results[param_name] = param.evaluate(results)
return results
def sum_harmonics(self, t: np.ndarray, context: Dict[str, Any]) -> np.ndarray:
"""Вычисляет сумму всех гармонических колебаний с учетом зависимостей"""
if not self.harmonics:
return np.zeros_like(t)
result = np.zeros_like(t)
for h in self.harmonics:
result += h.evaluate(t, context)
return result
def evaluate_main(self, x: np.ndarray, **kwargs) -> np.ndarray:
"""
Вычисляет основную формулу с заданным x и дополнительными параметрами
"""
if self.main_formula is None: if self.main_formula is None:
raise ValueError("Основная формула не установлена") raise ValueError("Основная формула не установлена")
param_values = self.calculate_all_parameters(x_values) # Вычисляем все параметры
context = self.evaluate_parameters(x)
context.update(kwargs)
# Подготовка аргументов для основной формулы # Добавляем сумму гармоник в контекст
args = [x_values] + [param_values[dep] for dep in self.main_dependencies] context['harmonic_sum'] = lambda t: self.sum_harmonics(t, context)
return self.main_formula(*args)
def clear_all_cache(self): # Получаем аргументы функции
"""Очистка всех кэшей""" sig = inspect.signature(self.main_formula)
for param in self.parameters.values():
param.clear_cache()
# Подготавливаем аргументы для вызова
call_args = {}
for param_name in sig.parameters:
if param_name in context:
call_args[param_name] = context[param_name]
elif param_name == 'x':
call_args['x'] = x
class Plotter: return self.main_formula(**call_args)
"""Класс для построения графиков"""
def __init__(self, model: FormulaModel): # ====================== УЛУЧШЕННЫЙ ВИЗУАЛИЗАТОР ======================
class ModelVisualizer:
"""Класс для визуализации модели с зависимостями"""
def __init__(self, model: MathematicalModel):
self.model = model self.model = model
self.figures = {}
def plot_parameter(self, param_name: str, x_range: tuple = (-10, 10), def plot_parameter(self, param_name: str,
num_points: int = 1000, title: str = None): 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: if param_name not in self.model.parameters:
raise ValueError(f"Параметр {param_name} не найден") raise ValueError(f"Параметр '{param_name}' не найден")
x_values = np.linspace(x_range[0], x_range[1], num_points)
param_values = self.model.calculate_all_parameters(x_values)
y_values = param_values[param_name]
plt.figure(figsize=(10, 6))
plt.plot(x_values, y_values, linewidth=2)
plt.grid(True, alpha=0.3)
param = self.model.parameters[param_name] param = self.model.parameters[param_name]
plt.title(title or f"Параметр {param_name}: {param.description}")
plt.xlabel("x")
plt.ylabel(f"{param_name} ({param.units})" if param.units else param_name)
plt.show() # Определяем диапазон x
if x_range is None:
if isinstance(param, IndependentParameter):
x_range = param.x_range
else:
# Для зависимых параметров нужно определить разумный диапазон
x_range = (0, 10) # По умолчанию
def plot_all_parameters(self, x_range: tuple = (-10, 10), num_points: int = 1000): x = np.linspace(x_range[0], x_range[1], 1000)
"""График всех параметров на одном рисунке"""
x_values = np.linspace(x_range[0], x_range[1], num_points)
param_values = self.model.calculate_all_parameters(x_values)
plt.figure(figsize=(14, 8)) # Вычисляем значение параметра
context = self.model.evaluate_parameters(x)
y = context[param_name]
for param_name, y_values in param_values.items(): # Создаем фигуру
plt.plot(x_values, y_values, label=param_name, linewidth=2) fig, ax = plt.subplots(figsize=figsize)
plt.grid(True, alpha=0.3) # Строим основной график
plt.title("Все параметры модели") ax.plot(x, y, color=param.color, linestyle=param.line_style,
plt.xlabel("x") linewidth=2, label=param_name)
plt.ylabel("Значения параметров")
plt.legend()
plt.show()
def plot_main_formula(self, x_range: tuple = (-10, 10), num_points: int = 1000, # Если нужно показать зависимости
title: str = "Основная формула"): if show_dependencies and isinstance(param, DependentParameter):
"""График основной формулы""" for dep_name in param.dependencies:
x_values = np.linspace(x_range[0], x_range[1], num_points) if dep_name in context:
y_values = self.model.calculate_main_formula(x_values) 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} (норм.)")
plt.figure(figsize=(10, 6)) ax.grid(True, alpha=0.3)
plt.plot(x_values, y_values, 'r-', linewidth=2, label='Основная формула') ax.set_title(title or f"График параметра: {param_name}\n{param.description}", fontsize=14)
plt.grid(True, alpha=0.3) ax.set_xlabel("x", fontsize=12)
plt.title(title) ax.set_ylabel("Значение", fontsize=12)
plt.xlabel("x") ax.legend(loc='best')
plt.ylabel("f(x)")
plt.legend()
plt.show()
def plot_dependency_graph(self): self.figures[f"param_{param_name}"] = fig
"""Визуализация графа зависимостей""" return fig
plt.figure(figsize=(12, 8))
pos = nx.spring_layout(self.model.dependency_graph, k=2, iterations=50)
nx.draw(self.model.dependency_graph, pos, def plot_parameter_with_dependencies(self, param_name: str,
with_labels=True, node_color='lightblue', figsize: Tuple[int, int] = (14, 10)) -> plt.Figure:
node_size=2000, font_size=10, font_weight='bold', """
arrows=True, arrowsize=20, edge_color='gray') Строит подробный график параметра и всех его зависимостей
"""
if param_name not in self.model.parameters:
raise ValueError(f"Параметр '{param_name}' не найден")
plt.title("Граф зависимостей параметров") param = self.model.parameters[param_name]
plt.show()
# Собираем все зависимости (рекурсивно)
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()))
def create_example_model(): # Определяем диапазон x
"""Создание примера модели для демонстрации""" x_range = (0, 10)
model = FormulaModel() 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)
param_a = Parameter( context = self.model.evaluate_parameters(x)
name="A",
formula=lambda x: np.sin(x) + 2,
description="Синусоидальная функция",
units="м/с"
)
# Зависимый параметр (зависит от A) # Создаем подграфики
param_b = Parameter( n_plots = len(all_deps) + 1
name="B", cols = min(3, n_plots)
formula=lambda x, a: np.cos(x) * a, rows = (n_plots + cols - 1) // cols
dependencies=["A"],
description="Функция, зависящая от A",
units="м/с²"
)
# Параметр, зависящий от B fig, axes = plt.subplots(rows, cols, figsize=figsize)
param_c = Parameter( axes = axes.flatten() if n_plots > 1 else [axes]
name="C",
formula=lambda x, b: np.exp(-x/10) * b,
dependencies=["B"],
description="Экспоненциальная функция от B",
units="Н"
)
# Параметр, зависящий от A и C # График целевого параметра
param_d = Parameter( axes[0].plot(x, context[param_name], color=param.color,
name="D", linestyle=param.line_style, linewidth=2.5)
formula=lambda x, a, c: (a**2 + c) / (1 + x**2), axes[0].grid(True, alpha=0.3)
dependencies=["A", "C"], axes[0].set_title(f"{param_name} (целевой)", fontsize=12)
description="Комбинированная функция", axes[0].set_xlabel("x")
units="Дж"
)
# Независимый параметр # Графики зависимостей
param_e = Parameter( for i, dep_name in enumerate(all_deps, 1):
name="E", if i < len(axes):
formula=lambda x: np.log(1 + np.abs(x)), dep_param = self.model.parameters[dep_name]
description="Логарифмическая функция", axes[i].plot(x, context[dep_name], color=dep_param.color,
units="К" 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 param in [param_a, param_b, param_c, param_d, param_e]: for j in range(len(all_deps) + 1, len(axes)):
model.add_parameter(param) axes[j].set_visible(False)
# Основная формула, использующая несколько параметров plt.tight_layout()
model.set_main_formula( self.figures[f"param_{param_name}_with_deps"] = fig
formula=lambda x, a, c, e: a * c + e**2, return fig
dependencies=["A", "C", "E"]
)
return model 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]
def analyze_model_complexity(model: FormulaModel): n_plots = len(self.model.harmonics) + 1
"""Анализ сложности модели""" fig, axes = plt.subplots(n_plots, 1, figsize=(figsize[0], figsize[1] * n_plots/3))
print("=== Анализ модели ===")
print(f"Количество параметров: {len(model.parameters)}")
# Анализ зависимостей # Индивидуальные гармоники
max_depth = 0 for i, h in enumerate(self.model.harmonics):
for param_name in model.parameters: 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: try:
depth = nx.shortest_path_length(model.dependency_graph, param_name) import networkx as nx
max_depth = max(max_depth, max(depth.values()) if depth else 0) except ImportError:
except: print("Для визуализации графа зависимостей установите networkx: pip install networkx")
pass return None
print(f"Максимальная глубина зависимостей: {max_depth}") G = nx.DiGraph()
print(f"Порядок вычисления: {model.get_calculation_order()}")
return max_depth # Добавляем узлы и ребра
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)
def benchmark_model(model: FormulaModel, x_range: tuple = (-10, 10), # Добавляем гармоники как узлы, если у них есть зависимости
num_points: int = 10000): for i, h in enumerate(self.model.harmonics):
"""Бенчмарк производительности модели""" deps = h.get_dependencies()
import time 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)
x_values = np.linspace(x_range[0], x_range[1], num_points) fig, ax = plt.subplots(figsize=figsize)
start_time = time.time() # Раскладка графа
param_values = model.calculate_all_parameters(x_values) pos = nx.spring_layout(G, k=2, iterations=50)
end_time = time.time()
print(f"Время вычисления всех параметров: {end_time - start_time:.4f} сек") # Рисуем граф
print(f"Точек данных: {num_points}") node_colors = [G.nodes[node].get('color', 'lightgray') for node in G.nodes]
print(f"Скорость: {num_points / (end_time - start_time):.0f} точек/сек") 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 = create_example_model()
plotter = Plotter(model)
# Анализ модели # Создаем модель
analyze_model_complexity(model) model = MathematicalModel()
# Константы
U0 = 1.0
h = 1.0
pi = np.pi
h_br = h / 2 / pi
# Построение графиков # Добавляем независимые параметры (базовые)
plotter.plot_dependency_graph() model.add_independent_parameter(
plotter.plot_all_parameters() name="n",
plotter.plot_main_formula() formula=lambda x: 1.0 * np.ones_like(x), # Константа
x_range=(0, 10),
color='gray',
description="Для дисперсионного уравнения"
)
# Графики отдельных параметров model.add_independent_parameter(
for param_name in ["A", "B", "C"]: name="x0_br",
plotter.plot_parameter(param_name) formula=lambda x: 1.0 * np.ones_like(x), # Константа
x_range=(0, 10),
color='blue',
description="Порог"
)
# Бенчмарк model.add_independent_parameter(
benchmark_model(model) name="f0",
formula=lambda x: 1.0 * np.ones_like(x), # Константа
x_range=(0, 10),
color='red',
description="Базовая частота"
)
# Добавляем зависимые параметры (выражаются через другие)
model.add_dependent_parameter(
name="Kn",
expression=lambda A0, time: A0 * np.exp(-0.1 * time), # Затухающая амплитуда
dependencies=["n", "x0_br"],
color='green',
description="Эффективная амплитуда (зависит от времени)"
)
model.add_dependent_parameter(
name="f_effective",
expression=lambda f0, time: f0 * (1 + 0.05 * np.sin(time)), # Модулированная частота
dependencies=["f0", "time"],
color='orange',
description="Эффективная частота (модулирована)"
)
model.add_dependent_parameter(
name="modulation_index",
expression=lambda A_effective, f_effective: A_effective * f_effective / 2,
dependencies=["A_effective", "f_effective"],
color='purple',
description="Индекс модуляции (произведение)"
)
# Добавляем гармоники, которые могут зависеть от параметров
model.add_harmonic(
amplitude="A_effective", # Использует параметр A_effective
frequency="f_effective", # Использует параметр f_effective
phase=0,
amplitude_depends_on=["A_effective"],
frequency_depends_on=["f_effective"]
)
model.add_harmonic(
amplitude=0.5,
frequency=2.0,
phase=np.pi/4
)
model.add_harmonic(
amplitude="modulation_index",
frequency=3.0,
phase=0,
amplitude_depends_on=["modulation_index"]
)
# Устанавливаем основную формулу
def main_formula(x, A_effective, f_effective, modulation_index, harmonic_sum):
"""
Основная формула: комбинация параметров и гармоник
"""
# Параметрическая часть
parametric_part = A_effective * np.sin(2 * np.pi * f_effective * x)
# Модуляционная часть
modulation_part = modulation_index * np.cos(2 * np.pi * x)
# Гармоническая часть
harmonic_part = harmonic_sum(x)
return parametric_part + modulation_part + harmonic_part
model.set_main_formula(main_formula)
# Создаем визуализатор
viz = ModelVisualizer(model)
# Строим граф зависимостей
print("Визуализация графа зависимостей...")
viz.plot_dependency_graph()
# Строим графики параметров с зависимостями
print("\nГрафики параметров с зависимостями...")
viz.plot_parameter("A_effective", show_dependencies=True)
viz.plot_parameter("f_effective", show_dependencies=True)
viz.plot_parameter_with_dependencies("modulation_index")
# Строим графики всех параметров
print("\nВсе параметры...")
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
axes = axes.flatten()
x_test = np.linspace(0, 10, 1000)
context = model.evaluate_parameters(x_test)
for i, (name, param) in enumerate(model.parameters.items()):
if i < len(axes):
axes[i].plot(x_test, context[name], color=param.color,
linestyle=param.line_style, linewidth=2)
axes[i].grid(True, alpha=0.3)
axes[i].set_title(f"{name}\n{param.description}")
axes[i].set_xlabel("x")
# Скрываем лишний подграфик
for j in range(len(model.parameters), len(axes)):
axes[j].set_visible(False)
plt.tight_layout()
# Строим графики гармоник с зависимостями
print("\nГармонические колебания...")
viz.plot_harmonics(t_range=(0, 5), x_for_dependencies=x_test)
# Строим график основной формулы
print("\nОсновная формула...")
fig, ax = plt.subplots(figsize=(12, 6))
y_main = model.evaluate_main(x_test)
ax.plot(x_test, y_main, 'b-', linewidth=2)
ax.grid(True, alpha=0.3)
ax.set_title("Результат работы основной формулы", fontsize=14)
ax.set_xlabel("x", fontsize=12)
ax.set_ylabel("F(x)", fontsize=12)
# Показываем все графики
plt.show()
# Выводим информацию о зависимостях
print("\nСтруктура зависимостей:")
deps = model.get_all_dependencies()
for param, dependencies in deps.items():
print(f" {param} зависит от: {', '.join(dependencies)}")
if __name__ == "__main__": if __name__ == "__main__":
main() main()