import numpy as np
import matplotlib.pyplot as plt

# A0, A1, A2, A3, A4, A5 = 0.4, 0.8, 2.4, 0.4, -0.2, -1.4
A0, A1, A2, A3, A4, A5 = 0.4, 0.8, 2.4, 0.4, 0.2, 1.4
start_point = [5, 5]
step_size = 2
N = 3
extrem = "min" # "max"/"min"
# Задані коефіцієнти
A = [A0, A1, A2, A3, A4, A5]  # Потрібно визначити значення A0, A1, A2, A3, A4, A5

# Функція, для якої шукаємо екстремум
def f(X1, X2):
    return A[0] + A[1]*X1 + A[2]*X2 + A[3]*X1*X2 + A[4]*X1**2 + A[5]*X2**2

# Ітераційний процес покоординатного спуску
def coordinate_descent(start_point, step_size, extrem, N):
    path = [start_point]
    count = 0
    while step_size > 0.01:
        current_point = path[-1]
        x1, x2 = current_point[0], current_point[1]

        # Оцінка значення функції в поточній точці
        current_value = f(x1, x2)

        # Пошук наступної точки
        next_points = [(x1 + step_size, x2), (x1 - step_size, x2), (x1, x2 + step_size), (x1, x2 - step_size)]
        next_values = [f(p[0], p[1]) for p in next_points]


        index = np.argmin(next_values) if extrem == "min" else np.argmax(next_values)
        next_point = next_points[index]

        # Перевірка на закінчення процесу
        if f(next_point[0], next_point[1]) >= current_value and extrem == "min":
            step_size /= N
        elif f(next_point[0], next_point[1]) <= current_value and extrem == "max":
            step_size /= N
        else:
            path.append(next_point)
        count += 1
    print(f"Extremum function on 8th step = {f(path[7][0], path[7][1])}")
    print(f"Computing costs = {len(path)}")
    return path

# Знаходження шляху знаходження екстремуму
path = coordinate_descent(start_point, step_size, extrem, N)

# Побудова графіка рівнів функції
x1_range = np.linspace(-10, 10, 400)
x2_range = np.linspace(-10, 10, 400)
X1, X2 = np.meshgrid(x1_range, x2_range)
Z = f(X1, X2)

plt.contour(X1, X2, Z, levels=20)
plt.plot([point[0] for point in path[:8]], [point[1] for point in path[:8]], marker='o', color='red')
plt.xlabel('X1')
plt.ylabel('X2')
plt.title('Contour Plot of the Function')
plt.grid(True)
plt.show()

