# 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 для продолжения...")