paper_quant_2/main.py
2026-02-18 11:50:20 +03:00

305 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()