optimization / old_code /univariate.py
joel-woodfield's picture
Add old code from gradio version
9357e05
import io
import gradio as gr
import matplotlib.pyplot as plt
import numexpr
import numpy as np
from PIL import Image
import logging
logging.basicConfig(
level=logging.INFO, # set minimum level to capture (DEBUG, INFO, WARNING, ERROR, CRITICAL)
format="%(asctime)s [%(levelname)s] %(message)s", # log format
)
logger = logging.getLogger("ELVIS")
from optimisers import get_gradient_1d, get_hessian_1d, get_optimizer_trajectory_1d
class Univariate:
DEFAULT_UNIVARIATE = "x ** 2"
DEFAULT_INIT_X = 0.5
def __init__(self, width, height):
self.canvas_width = width
self.canvas_height = height
self.optimiser_type = "Gradient Descent"
self.learning_rate = 0.1
self.num_steps = 20
self.momentum = 0
self.function = self.DEFAULT_UNIVARIATE
self.initial_x = self.DEFAULT_INIT_X
self.trajectory_x, self.trajectory_y = get_optimizer_trajectory_1d(
self.DEFAULT_UNIVARIATE,
self.DEFAULT_INIT_X,
self.optimiser_type,
self.learning_rate,
self.momentum,
self.num_steps,
)
self.trajectory_idx = 0
self.plots = []
self.generate_plots()
def generate_plots(self):
self.plots.clear()
fig, ax = plt.subplots()
for idx in range(self.num_steps):
traj_x_min = np.min(self.trajectory_x[:idx + 1])
traj_x_max = np.max(self.trajectory_x[:idx + 1])
x_radius = np.maximum(np.abs(traj_x_min), np.abs(traj_x_max))
if x_radius > 1:
x = np.linspace(-1.2 * x_radius, 1.2 * x_radius, 100)
else:
x = np.linspace(-1, 1, 100)
try:
y = numexpr.evaluate(self.function, local_dict={'x': x})
except Exception as e:
logger.error("Error evaluating function '%s': %s", function, e)
y = np.zeros_like(x)
ax.clear()
ax.plot(x, y)
ax.set_xlabel("x")
ax.set_ylabel("f(x)")
ax.plot(self.trajectory_x[:idx + 1], self.trajectory_y[:idx + 1], marker='o', color='indianred')
ax.plot(self.trajectory_x[idx], self.trajectory_y[idx], marker='o', color='red')
buf = io.BytesIO()
fig.savefig(buf, format="png", bbox_inches="tight", pad_inches=0)
plt.close(fig)
buf.seek(0)
img = Image.open(buf)
# Append the generated plot to the list
self.plots.append(img)
def update_plot(self):
plot = self.plots[self.trajectory_idx]
self.univariate_plot = plot
return plot
def update_optimiser_type(self, optimiser_type):
self.optimiser_type = optimiser_type
def update_trajectory(self):
trajectory_x, trajectory_y = get_optimizer_trajectory_1d(
self.function,
self.initial_x,
self.optimiser_type,
self.learning_rate,
self.momentum,
self.num_steps,
)
self.trajectory_x = trajectory_x
self.trajectory_y = trajectory_y
def update_trajectory_slider(self, trajectory_idx):
self.trajectory_idx = trajectory_idx
def update_learning_rate(self, learning_rate):
self.learning_rate = learning_rate
def update_initial_x(self, initial_x):
self.initial_x = initial_x
def update_function(self, function):
self.function = function
def show_relevant_params(self, optimiser_type):
if optimiser_type == "Gradient Descent":
learning_rate = gr.update(visible=True)
hessian = gr.update(visible=False)
momentum = gr.update(visible=True)
else:
learning_rate = gr.update(visible=False)
hessian = gr.update(visible=True)
momentum = gr.update(visible=False)
return hessian, learning_rate, momentum
def handle_trajectory_change(self):
self.update_trajectory()
self.generate_plots()
self.handle_slider_change(0) # reset slider
self.update_plot()
def handle_optimiser_type_change(self, optimiser_type):
self.update_optimiser_type(optimiser_type)
self.handle_trajectory_change()
hessian_update, learning_rate_update, momentum_update = self.show_relevant_params(optimiser_type)
return self.trajectory_idx, hessian_update, learning_rate_update, momentum_update, self.univariate_plot
def handle_learning_rate_change(self, learning_rate):
self.update_learning_rate(learning_rate)
self.handle_trajectory_change()
return self.trajectory_idx, self.univariate_plot
def handle_momentum_change(self, momentum):
self.momentum = momentum
self.handle_trajectory_change()
return self.trajectory_idx, self.univariate_plot
def handle_slider_change(self, trajectory_idx):
self.update_trajectory_slider(trajectory_idx)
self.update_plot()
return self.univariate_plot
def handle_trajectory_button(self):
if self.trajectory_idx < self.num_steps - 1:
self.trajectory_idx += 1
# plot is updated from slider changing
return self.trajectory_idx
def handle_initial_x_change(self, initial_x):
self.update_initial_x(initial_x)
self.handle_trajectory_change()
return self.trajectory_idx, self.univariate_plot
def handle_function_change(self, function):
self.update_function(function)
self.handle_trajectory_change()
gradient = f"{get_gradient_1d(function)}"
hessian = f"{get_hessian_1d(function)}"
return self.trajectory_idx, gradient, hessian, self.univariate_plot
def reset(self):
self.optimiser_type = "Gradient Descent"
self.learning_rate = 0.1
self.num_steps = 20
self.function = self.DEFAULT_UNIVARIATE
self.initial_x = self.DEFAULT_INIT_X
self.trajectory_x, self.trajectory_y = get_optimizer_trajectory_1d(
self.DEFAULT_UNIVARIATE,
self.DEFAULT_INIT_X,
self.optimiser_type,
self.learning_rate,
self.momentum,
self.num_steps,
)
self.trajectory_idx = 0
self.plots = []
self.generate_plots()
def build(self):
with gr.Tab("Univariate"):
with gr.Row():
with gr.Column(scale=2):
self.univariate_plot = gr.Image(
value=self.update_plot(),
container=True,
)
with gr.Column(scale=1):
with gr.Tab("Settings"):
function = gr.Textbox(label="Function", value=self.DEFAULT_UNIVARIATE, interactive=True)
gradient = gr.Textbox(
label="Derivative",
value=f"{get_gradient_1d(self.DEFAULT_UNIVARIATE)}",
interactive=False,
)
hessian = gr.Textbox(
label="Second Derivative",
value=f"{get_hessian_1d(self.DEFAULT_UNIVARIATE)}",
interactive=False,
visible=False,
)
optimiser_type = gr.Dropdown(
label="Optimiser",
choices=["Gradient Descent", "Newton"],
value="Gradient Descent",
interactive=True,
)
initial_x = gr.Number(label="Initial X", value=self.DEFAULT_INIT_X, interactive=True)
with gr.Row():
learning_rate = gr.Number(label="Learning Rate", value=self.learning_rate, interactive=True)
momentum = gr.Number(label="Momentum", value=self.momentum, interactive=True)
with gr.Tab("Optimize"):
trajectory_slider = gr.Slider(
label="Optimisation Step",
minimum=0,
maximum=self.num_steps - 1,
step=1,
value=0,
interactive=True,
)
trajectory_button = gr.Button("Optimisation Step")
function.submit(self.handle_function_change, inputs=[function], outputs=[trajectory_slider, gradient, hessian, self.univariate_plot])
initial_x.submit(self.handle_initial_x_change, inputs=[initial_x], outputs=[trajectory_slider, self.univariate_plot])
learning_rate.submit(self.handle_learning_rate_change, inputs=[learning_rate], outputs=[trajectory_slider, self.univariate_plot])
momentum.submit(self.handle_momentum_change, inputs=[momentum], outputs=[trajectory_slider, self.univariate_plot])
optimiser_type.change(
self.handle_optimiser_type_change,
inputs=[optimiser_type],
outputs=[trajectory_slider, hessian, learning_rate, momentum, self.univariate_plot]
)
trajectory_slider.change(self.handle_slider_change, inputs=[trajectory_slider], outputs=[self.univariate_plot])
trajectory_button.click(self.handle_trajectory_button, outputs=[trajectory_slider])