Initial commit: планирование эксперимента и оптимизация смесей
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
# 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 для продолжения...")
|
||||
Reference in New Issue
Block a user