import numpy as np import matplotlib.pyplot as plt from typing import Dict, List, Callable, Any, Optional import networkx as nx from functools import lru_cache import sympy as sp from collections import defaultdict class Parameter: """Класс для представления параметра с его формулой и зависимостями""" def __init__(self, name: str, formula: Callable, dependencies: List[str] = None, description: str = "", units: str = ""): self.name = name 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: """Вычисление значений параметра для заданных x""" # Проверка кэша cache_key = (tuple(x_values), tuple(sorted(param_values.items()))) if cache_key in self.cache: return self.cache[cache_key] # Вычисление if self.dependencies: # Подготовка аргументов для формулы args = [x_values] + [param_values[dep] for dep in self.dependencies] result = self.formula(*args) else: result = self.formula(x_values) # Сохранение в кэш self.cache[cache_key] = result return result def clear_cache(self): """Очистка кэша параметра""" self.cache.clear() class FormulaModel: """Основной класс для управления моделью с параметрами""" def __init__(self): self.parameters: Dict[str, Parameter] = {} self.main_formula: Optional[Callable] = None self.dependency_graph = nx.DiGraph() def add_parameter(self, parameter: Parameter): """Добавление параметра в модель""" self.parameters[parameter.name] = parameter # Обновление графа зависимостей self.dependency_graph.add_node(parameter.name) for dep in parameter.dependencies: self.dependency_graph.add_edge(dep, parameter.name) # Проверка на циклические зависимости if not nx.is_directed_acyclic_graph(self.dependency_graph): raise ValueError(f"Циклическая зависимость обнаружена при добавлении параметра {parameter.name}") def set_main_formula(self, formula: Callable, dependencies: List[str]): """Установка основной формулы модели""" self.main_formula = formula self.main_dependencies = dependencies def get_calculation_order(self) -> List[str]: """Получение порядка вычисления параметров с учетом зависимостей""" try: return list(nx.topological_sort(self.dependency_graph)) except nx.NetworkXError: raise ValueError("Невозможно определить порядок вычисления из-за циклических зависимостей") def calculate_all_parameters(self, x_values: np.ndarray) -> Dict[str, np.ndarray]: """Вычисление всех параметров в правильном порядке""" param_values = {} calculation_order = self.get_calculation_order() for param_name in calculation_order: if param_name in self.parameters: param = self.parameters[param_name] param_values[param_name] = param.calculate(x_values, param_values) return param_values def calculate_main_formula(self, x_values: np.ndarray) -> np.ndarray: """Вычисление основной формулы""" if self.main_formula is None: raise ValueError("Основная формула не установлена") param_values = self.calculate_all_parameters(x_values) # Подготовка аргументов для основной формулы args = [x_values] + [param_values[dep] for dep in self.main_dependencies] return self.main_formula(*args) def clear_all_cache(self): """Очистка всех кэшей""" for param in self.parameters.values(): param.clear_cache() class Plotter: """Класс для построения графиков""" def __init__(self, model: FormulaModel): self.model = model def plot_parameter(self, param_name: str, x_range: tuple = (-10, 10), num_points: int = 1000, title: str = None): """График отдельного параметра""" if param_name not in self.model.parameters: 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] 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() def plot_all_parameters(self, x_range: tuple = (-10, 10), num_points: int = 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)) for param_name, y_values in param_values.items(): plt.plot(x_values, y_values, label=param_name, linewidth=2) plt.grid(True, alpha=0.3) plt.title("Все параметры модели") plt.xlabel("x") plt.ylabel("Значения параметров") plt.legend() plt.show() def plot_main_formula(self, x_range: tuple = (-10, 10), num_points: int = 1000, title: str = "Основная формула"): """График основной формулы""" x_values = np.linspace(x_range[0], x_range[1], num_points) y_values = self.model.calculate_main_formula(x_values) plt.figure(figsize=(10, 6)) plt.plot(x_values, y_values, 'r-', linewidth=2, label='Основная формула') plt.grid(True, alpha=0.3) plt.title(title) plt.xlabel("x") plt.ylabel("f(x)") plt.legend() plt.show() def plot_dependency_graph(self): """Визуализация графа зависимостей""" plt.figure(figsize=(12, 8)) pos = nx.spring_layout(self.model.dependency_graph, k=2, iterations=50) nx.draw(self.model.dependency_graph, pos, with_labels=True, node_color='lightblue', node_size=2000, font_size=10, font_weight='bold', arrows=True, arrowsize=20, edge_color='gray') plt.title("Граф зависимостей параметров") plt.show() # Пример использования и заготовки функций def create_example_model(): """Создание примера модели для демонстрации""" model = FormulaModel() # Независимые параметры param_a = Parameter( name="A", formula=lambda x: np.sin(x) + 2, description="Синусоидальная функция", units="м/с" ) # Зависимый параметр (зависит от A) param_b = Parameter( name="B", formula=lambda x, a: np.cos(x) * a, dependencies=["A"], description="Функция, зависящая от A", units="м/с²" ) # Параметр, зависящий от B param_c = Parameter( name="C", formula=lambda x, b: np.exp(-x/10) * b, dependencies=["B"], description="Экспоненциальная функция от B", units="Н" ) # Параметр, зависящий от A и C param_d = Parameter( name="D", formula=lambda x, a, c: (a**2 + c) / (1 + x**2), dependencies=["A", "C"], description="Комбинированная функция", units="Дж" ) # Независимый параметр param_e = Parameter( name="E", formula=lambda x: np.log(1 + np.abs(x)), description="Логарифмическая функция", units="К" ) # Добавление параметров в модель for param in [param_a, param_b, param_c, param_d, param_e]: model.add_parameter(param) # Основная формула, использующая несколько параметров model.set_main_formula( formula=lambda x, a, c, e: a * c + e**2, dependencies=["A", "C", "E"] ) return model # Утилитарные функции def analyze_model_complexity(model: FormulaModel): """Анализ сложности модели""" print("=== Анализ модели ===") print(f"Количество параметров: {len(model.parameters)}") # Анализ зависимостей max_depth = 0 for param_name in model.parameters: try: depth = nx.shortest_path_length(model.dependency_graph, param_name) max_depth = max(max_depth, max(depth.values()) if depth else 0) except: pass print(f"Максимальная глубина зависимостей: {max_depth}") print(f"Порядок вычисления: {model.get_calculation_order()}") return max_depth def benchmark_model(model: FormulaModel, x_range: tuple = (-10, 10), num_points: int = 10000): """Бенчмарк производительности модели""" import time x_values = np.linspace(x_range[0], x_range[1], num_points) start_time = time.time() param_values = model.calculate_all_parameters(x_values) end_time = time.time() print(f"Время вычисления всех параметров: {end_time - start_time:.4f} сек") print(f"Точек данных: {num_points}") print(f"Скорость: {num_points / (end_time - start_time):.0f} точек/сек") # Основная функция для демонстрации def main(): """Основная функция для демонстрации работы скрипта""" # Создание модели model = create_example_model() plotter = Plotter(model) # Анализ модели analyze_model_complexity(model) # Построение графиков plotter.plot_dependency_graph() plotter.plot_all_parameters() plotter.plot_main_formula() # Графики отдельных параметров for param_name in ["A", "B", "C"]: plotter.plot_parameter(param_name) # Бенчмарк benchmark_model(model) if __name__ == "__main__": main()