Initial commit: планирование эксперимента и оптимизация смесей

This commit is contained in:
2026-07-23 15:12:32 +05:00
commit 025421193d
17 changed files with 1634 additions and 0 deletions
+135
View File
@@ -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 для продолжения...")