136 lines
5.7 KiB
Python
136 lines
5.7 KiB
Python
# ascent.py
|
|
"""
|
|
Крутое восхождение (градиентный метод) для поиска оптимума.
|
|
"""
|
|
import numpy as np
|
|
from core.table_io import handle_table_output
|
|
from core.statistics import compute_regression, compute_gradient_points
|
|
|
|
def ascent_menu(mixture, design, responses):
|
|
if not design.plan or not responses:
|
|
print("Нет данных для крутого восхождения: план или отклики пусты.")
|
|
input("Нажмите Enter для продолжения...")
|
|
return
|
|
|
|
active_indices = design._active_indices()
|
|
k = len(active_indices)
|
|
if k < 2:
|
|
print("Для крутого восхождения необходимо минимум 2 активных фактора.")
|
|
input("Нажмите Enter для продолжения...")
|
|
return
|
|
|
|
if not mixture.ings or len(mixture.ings) < 2:
|
|
print("В смеси нет изменяемых ингредиентов.")
|
|
return
|
|
|
|
base_levels = []
|
|
factor_names = []
|
|
for idx in active_indices:
|
|
ing_index = idx + 1
|
|
if ing_index >= len(mixture.ings):
|
|
print(f"Ошибка: индекс ингредиента {ing_index} выходит за пределы списка.")
|
|
return
|
|
factor_names.append(mixture.ings[ing_index].name)
|
|
base_levels.append(mixture.ings[ing_index].value)
|
|
print(f"\nБазовые уровни факторов: {dict(zip(factor_names, base_levels))}")
|
|
|
|
print("\nДоступные отклики:")
|
|
for i, resp in enumerate(responses, start=1):
|
|
print(f"{i}. {resp['name']}")
|
|
try:
|
|
resp_idx = int(input("Выберите номер отклика для оптимизации: ")) - 1
|
|
if resp_idx < 0 or resp_idx >= len(responses):
|
|
print("Неверный номер.")
|
|
return
|
|
response = responses[resp_idx]
|
|
except ValueError:
|
|
print("Введите число.")
|
|
return
|
|
|
|
plan_matrix = np.array([[row[j] for j in active_indices] for row in design.plan])
|
|
N = plan_matrix.shape[0]
|
|
X = np.hstack([np.ones((N, 1)), plan_matrix])
|
|
y = np.array(response["values"])
|
|
if len(y) != N:
|
|
print("Ошибка: число значений отклика не совпадает с числом опытов.")
|
|
return
|
|
|
|
reg = compute_regression(X, y)
|
|
beta = reg["beta"]
|
|
if np.isnan(beta).any():
|
|
print("Ошибка расчёта коэффициентов регрессии.")
|
|
return
|
|
|
|
print("\n=== Коэффициенты модели (МНК) ===")
|
|
headers = ["Фактор", "Коэффициент"]
|
|
rows = [["Свободный член", f"{beta[0]:.6f}"]]
|
|
for i, name in enumerate(factor_names):
|
|
rows.append([name, f"{beta[i+1]:.6f}"])
|
|
handle_table_output(headers, rows, title="", ask_each_time=True)
|
|
|
|
print("\nВведите шаг движения (в % для первого фактора).")
|
|
print("Шаг для остальных факторов будет пропорционален их коэффициентам.")
|
|
try:
|
|
step = float(input("Шаг (положительное число): "))
|
|
if step <= 0:
|
|
print("Шаг должен быть положительным.")
|
|
return
|
|
except ValueError:
|
|
print("Введите число.")
|
|
return
|
|
|
|
try:
|
|
num_steps = int(input("Количество шагов (например, 5): "))
|
|
if num_steps <= 0:
|
|
print("Количество шагов должно быть положительным.")
|
|
return
|
|
except ValueError:
|
|
print("Введите целое число.")
|
|
return
|
|
|
|
direction = input("Максимизировать отклик? (y/n, по умолчанию y): ").strip().lower()
|
|
maximize = direction != 'n'
|
|
|
|
try:
|
|
points = compute_gradient_points(
|
|
beta, base_levels, step, num_steps,
|
|
factor_names=factor_names,
|
|
maximize=maximize
|
|
)
|
|
except Exception as e:
|
|
print(f"Ошибка: {e}")
|
|
return
|
|
|
|
headers = ["Шаг"] + factor_names + ["Предсказанный отклик"]
|
|
rows = []
|
|
for pt in points:
|
|
row = [str(pt["Шаг"])] + [f"{pt[name]:.4f}" for name in factor_names] + [f"{pt['Предсказанный отклик']:.4f}"]
|
|
rows.append(row)
|
|
handle_table_output(
|
|
headers, rows,
|
|
title=f"Точки крутого восхождения (направление: {'максимизация' if maximize else 'минимизация'})"
|
|
)
|
|
|
|
print("\nВы можете выбрать одну из точек и установить её как новый состав.")
|
|
choice = input("Установить состав из точки? (введите номер шага или Enter для пропуска): ").strip()
|
|
if choice:
|
|
try:
|
|
step_num = int(choice) - 1
|
|
if 0 <= step_num < len(points):
|
|
pt = points[step_num]
|
|
for name in factor_names:
|
|
for idx, info in enumerate(mixture.ings):
|
|
if info.name == name:
|
|
mixture.ings[idx].value = pt[name]
|
|
break
|
|
mixture.comp_solvent_percent()
|
|
print("Состав обновлён. Текущие проценты:")
|
|
for info in mixture.ings:
|
|
print(f" {info.name}: {info.value:.2f}%")
|
|
else:
|
|
print("Неверный номер.")
|
|
except ValueError:
|
|
print("Ошибка ввода.")
|
|
|
|
input("Нажмите Enter для продолжения...")
|