optimization/algorithm/optimization/.ipynb_checkpoints/gd-checkpoint.py

438 lines
23 KiB
Python

import copy
import time
import sympy
import numpy as np
from scipy.misc import derivative
from sympy import symbols, sympify, lambdify, diff
import ipywidgets as widgets
from IPython.display import display, clear_output
from tqdm import tqdm
import matplotlib.pyplot as plt
import plotly.graph_objects as go
import plotly.io as pio
import warnings
warnings.filterwarnings("ignore")
class gradient_descent_1d(object):
def __init__(self, environ:str="jupyterlab"):
pio.renderers.default = environ # 'notebook' or 'colab' or 'jupyterlab'
self.wg_expr = widgets.Text(value="x**3 - x**(1/2)",
description="Expression:",
style={'description_width': 'initial'})
self.wg_x0 = widgets.FloatText(value="2",
description="Startpoint:",
style={'description_width': 'initial'})
self.wg_lr = widgets.FloatText(value="1e-1",
description="step size:",
style={'description_width': 'initial'})
self.wg_epsilon = widgets.FloatText(value="1e-5",
description="criterion:",
style={'description_width': 'initial'})
self.wg_max_iter = widgets.IntText(value="1000",
description="max iteration",
style={'description_width': 'initial'})
self.button_compute = widgets.Button(description="Compute")
self.button_plot = widgets.Button(description="Plot")
self.compute_output = widgets.Output()
self.plot_output = widgets.Output()
self.params_lvbox = widgets.VBox([self.wg_expr, self.wg_x0, self.wg_lr])
self.params_rvbox = widgets.VBox([self.wg_epsilon, self.wg_max_iter])
self.params_box = widgets.HBox([self.params_lvbox, self.params_rvbox], description="Parameters")
self.button_box = widgets.HBox([self.button_compute, self.button_plot], description="operations")
self.config = widgets.VBox([self.params_box, self.button_box])
self.initialization()
def initialization(self):
display(self.config)
self.button_compute.on_click(self.compute)
display(self.compute_output)
self.button_plot.on_click(self.plot)
display(self.plot_output)
def compute(self, *args):
with self.compute_output:
xn = self.wg_x0.value
x = symbols("x")
expr = sympify(self.wg_expr.value)
f = lambdify(x, expr)
df = lambdify(x, diff(expr, x))
self.xn_list, self.df_list = [], []
for n in tqdm(range(0, self.wg_max_iter.value)):
gradient = df(xn)
self.xn_list.append(xn)
self.df_list.append(gradient)
if abs (gradient < self.wg_epsilon.value):
clear_output(wait=True)
print("Found solution of {} after".format(expr), n, "iterations")
print("x* = {}".format(xn))
return None
xn = xn - self.wg_lr.value * gradient
clear_output(wait=True)
display("Exceeded maximum iterations. No solution found.")
return None
def plot(self, *args):
with self.plot_output:
clear_output(wait=True)
x0 = float(self.wg_x0.value)
x = symbols("x")
expr = sympify(self.wg_expr.value)
f = lambdify(x, sympify(expr), "numpy")
xx1 = np.arange(np.array(self.xn_list).min()*0.5, np.array(self.xn_list).max()*1.5, 0.05)
fx = f(xx1)
f_xn = f(np.array(self.xn_list))
fig = go.Figure()
fig.add_scatter(x=xx1, y=fx)
frames = []
frames.append({'data':copy.deepcopy(fig['data']),'name':f'frame{0}'})
fig.add_traces(go.Scatter(x=None, y=None, mode="lines + markers", line={"color":"#de1032", "width":5}))
frames = [go.Frame(data= [go.Scatter(x=np.array(self.xn_list)[:k], y=f_xn)],traces= [1],name=f'frame{k+2}')for k in range(len(f_xn))]
fig.update(frames=frames)
fig.update_layout(updatemenus=[dict(type="buttons",buttons=[dict(label="Play",method="animate",args=[None])])])
fig.show()
class gradient_descent_2d(object):
def __init__(self, environ:str="jupyterlab"):
pio.renderers.default = environ # 'notebook' or 'colab' or 'jupyterlab'
self.wg_expr = widgets.Dropdown(options=[("(sin(x1) - 2) + (sin(x2) - 2) ** 2", "(sin(x1) - 2) + (sin(x2) - 2) ** 2"),
("(sin(x1) - 2) ** (1/2) + (sin(x2) - 2) ** 2", "(sin(x1) - 2) ** (1/2) + (sin(x2) - 2) ** 2"),
("(sin(x1) - 2) ** 2 + (sin(x2) - 2) ** 2", "(sin(x1) - 2) ** 2 + (sin(x2) - 2) ** 2")],
value="(sin(x1) - 2) ** 2 + (sin(x2) - 2) ** 2", descrption="Expression")
self.wg_x0 = widgets.Text(value="5,5",
description="Startpoint:")
self.wg_lr = widgets.FloatText(value="1e-1",
description="step size:")
self.wg_epsilon = widgets.FloatText(value="1e-5",
description="criterion:")
self.wg_max_iter = widgets.IntText(value="1000",
description="max iteration")
self.button_compute = widgets.Button(description="Compute")
self.button_plot = widgets.Button(description="Plot")
self.compute_output = widgets.Output()
self.plot_output = widgets.Output()
self.params_lvbox = widgets.VBox([self.wg_x0, self.wg_lr])
self.params_rvbox = widgets.VBox([self.wg_epsilon, self.wg_max_iter])
self.exp_box = widgets.HBox([self.wg_expr])
self.params_box = widgets.HBox([self.params_lvbox, self.params_rvbox], description="Parameters")
self.button_box = widgets.HBox([self.button_compute, self.button_plot], description="operations")
self.config = widgets.VBox([self.exp_box, self.params_box, self.button_box])
self.initialization()
def initialization(self):
display(self.config)
self.button_compute.on_click(self.compute)
display(self.compute_output)
self.button_plot.on_click(self.plot)
display(self.plot_output)
def compute(self, *args):
with self.compute_output:
x0 = np.array(self.wg_x0.value.split(","), dtype=float)
xn = x0
x1 = symbols("x1")
x2 = symbols("x2")
expr = sympify(self.wg_expr.value)
self.xn_list, self.df_list = [], []
for n in tqdm(range(0, self.wg_max_iter.value)):
gradient = np.array([diff(expr, x1).subs(x1, xn[0]).subs(x2, xn[1]),
diff(expr, x2).subs(x1, xn[0]).subs(x2, xn[1])], dtype=float)
self.xn_list.append(xn)
self.df_list.append(gradient)
if np.linalg.norm(gradient, ord=2) < self.wg_epsilon.value:
clear_output(wait=True)
print("Found solution of {} after".format(expr), n, "iterations")
print("x* = [{}, {}]".format(xn[0], xn[1]))
return None
xn = xn - self.wg_lr.value * gradient
clear_output(wait=True)
display("Exceeded maximum iterations. No solution found.")
return None
def plot(self, *args):
with self.plot_output:
clear_output(wait=True)
x0 = np.array(self.wg_x0.value.split(","), dtype=float)
x1 = symbols("x1")
x2 = symbols("x2")
expr = sympify(self.wg_expr.value)
xx1 = np.arange(np.array(self.xn_list)[:, 0].min() * 0.5, np.array(self.xn_list)[:, 0].max() * 1.5, 0.1)
xx2 = np.arange(np.array(self.xn_list)[:, 1].min() * 0.5, np.array(self.xn_list)[:, 1].max() * 1.5, 0.1)
xx1_tangent = np.arange(np.array(self.xn_list)[:, 0].min(), np.array(self.xn_list)[:, 0].max(), 0.1)
xx2_tangent = np.arange(np.array(self.xn_list)[:, 1].min(), np.array(self.xn_list)[:, 1].max(), 0.1)
xx1_o, xx2_o = xx1, xx2
xx1, xx2 = np.meshgrid(xx1, xx2)
xx1_tangent, xx2_tangent = np.meshgrid(xx1_tangent, xx2_tangent)
f = lambdify((x1, x2), expr, "numpy")
fx = f(xx1, xx2)
f_xn = f(np.array(self.xn_list)[:, 0], np.array(self.xn_list)[:, 1])
partial_x1 = lambdify((x1, x2), diff(expr, x1), "numpy")
partial_x2 = lambdify((x1, x2), diff(expr, x2), "numpy")
plane = partial_x1(np.array(self.xn_list)[:, 0], np.array(self.xn_list)[:, 1]) * (x1 - np.array(self.xn_list)[:, 0]) + partial_x2(np.array(self.xn_list)[:, 0], np.array(self.xn_list)[:, 1]) * (x2 - np.array(self.xn_list)[:, 0]) + f_xn
z = [lambdify((x1, x2), plane[i], "numpy")(xx1_tangent, xx2_tangent) for i in range(0, len(plane))]
frames, steps = [], []
for k in range(len(f_xn)):
tmp_trace1 = go.Scatter3d(x=np.array(self.xn_list)[:k,0], y=np.array(self.xn_list)[:k,1], z=f_xn)
tmp_trace2 = go.Surface(x=xx1_tangent, y=xx2_tangent, z=z[k], showscale=True, opacity=0.5)
frame = go.Frame(dict(data=[tmp_trace1, tmp_trace2], name=f'frame{k+1}'), traces=[1, 2])
frames.append(frame)
step = dict(
method="update",
args=[{"visible": [True]},
{"title": "Slider switched to step: " + str(k+1)}], # layout attribute
)
steps.append(step)
sliders = [dict(steps= [dict(method= 'animate',
args= [[f'frame{k+1}'],
dict(mode= 'immediate',
frame= dict( duration=0, redraw= True ),
transition=dict( duration=0)
)
],
#label='Date : {}'.format(date_range[k])
) for k in range(0,len(frames))],
transition= dict(duration=0),
x=0,
y=0,
currentvalue=dict(font=dict(size=12), visible=True, xanchor= 'center'),
len=1.0)
]
trace1 = go.Surface(x=xx1, y=xx2, z=fx, showscale=False, opacity=0.8)
trace2 = go.Scatter3d(x=None, y=None, z=None)
trace3 = go.Surface(x=None, y=None, z=None, showscale=False, opacity=0.5, colorscale='Blues')
fig = go.Figure(data=[trace1, trace2, trace3], frames=frames)
fig.update_layout(updatemenus=[dict(type="buttons", buttons=[dict(label="Play", method="animate", args=[None, dict(fromcurrent=True)]), \
dict(label="Pause", method="animate", args=[[None], \
dict(fromcurrent=True, mode='immediate', transition= {'duration': 0}, frame=dict(redraw=True, duration=0))])])],
margin=dict(r=20, l=10, b=10, t=10), sliders=sliders)
fig.show()
class gradient_descent_2d_custom(object):
def __init__(self, environ:str="jupyterlab"):
pio.renderers.default = environ # 'notebook' or 'colab' or 'jupyterlab'
self.wg_expr = widgets.Text(value="(sin(x1) - 2) ** 2 + (sin(x2) - 2) ** 2", description="Expression:")
self.wg_x0 = widgets.Text(value="5,5", description="Startpoint:")
self.wg_lr = widgets.FloatText(value="1e-1", description="step size:")
self.wg_epsilon = widgets.FloatText(value="1e-5", description="criterion:")
self.wg_max_iter = widgets.IntText(value="1000", description="max iteration")
self.button_compute = widgets.Button(description="Compute")
self.button_plot = widgets.Button(description="Plot")
self.compute_output = widgets.Output()
self.plot_output = widgets.Output()
self.params_lvbox = widgets.VBox([self.wg_expr, self.wg_x0, self.wg_lr])
self.params_rvbox = widgets.VBox([self.wg_epsilon, self.wg_max_iter])
self.params_box = widgets.HBox([self.params_lvbox, self.params_rvbox], description="Parameters")
self.button_box = widgets.HBox([self.button_compute, self.button_plot], description="operations")
self.config = widgets.VBox([self.params_box, self.button_box])
self.initialization()
def initialization(self):
display(self.config)
self.button_compute.on_click(self.compute)
display(self.compute_output)
self.button_plot.on_click(self.plot)
display(self.plot_output)
def compute(self, *args):
with self.compute_output:
x0 = np.array(self.wg_x0.value.split(","), dtype=float)
xn = x0
x1 = symbols("x1")
x2 = symbols("x2")
expr = sympify(self.wg_expr.value)
self.xn_list, self.df_list = [], []
for n in tqdm(range(0, self.wg_max_iter.value)):
gradient = np.array([diff(expr, x1).subs(x1, xn[0]).subs(x2, xn[1]),
diff(expr, x2).subs(x1, xn[0]).subs(x2, xn[1])], dtype=float)
self.xn_list.append(xn)
self.df_list.append(gradient)
if np.linalg.norm(gradient, ord=2) < self.wg_epsilon.value:
clear_output(wait=True)
print("Found solution of {} after".format(expr), n, "iterations")
print("x* = [{}, {}]".format(xn[0], xn[1]))
return None
xn = xn - self.wg_lr.value * gradient
clear_output(wait=True)
display("Exceeded maximum iterations. No solution found.")
return None
def plot(self, *args):
with self.plot_output:
clear_output(wait=True)
x0 = np.array(self.wg_x0.value.split(","), dtype=float)
x1 = symbols("x1")
x2 = symbols("x2")
expr = sympify(self.wg_expr.value)
xx1 = np.arange(np.array(self.xn_list)[:, 0].min()*0.5, np.array(self.xn_list)[:, 0].max()*1.5, 0.05)
xx2 = np.arange(np.array(self.xn_list)[:, 1].min()*0.5, np.array(self.xn_list)[:, 1].max()*1.5, 0.05)
xx1, xx2 = np.meshgrid(xx1, xx2)
f = lambdify((x1, x2), expr, "numpy")
fx = f(xx1, xx2)
f_xn = f(np.array(self.xn_list)[:, 0], np.array(self.xn_list)[:, 1])
frames, steps = [], []
for k in range(len(f_xn)):
#frame = go.Frame(data=[go.Surface(x=xx1, y=xx2, z=fx, showscale=True, opacity=0.8)])
#fig.add_trace(go.Scatter3d(x=np.array(self.xn_list)[:k, 0], y=np.array(self.xn_list)[:k, 1], z=f_xn))
frame = go.Frame(dict(data=[go.Scatter3d(x=np.array(self.xn_list)[:k,0], y=np.array(self.xn_list)[:k,1], z=f_xn)], name=f'frame{k+1}'), traces=[1])
frames.append(frame)
step = dict(
method="update",
args=[{"visible": [True]},
{"title": "Slider switched to step: " + str(k+1)}], # layout attribute
)
steps.append(step)
sliders = [dict(steps= [dict(method= 'animate',
args= [[f'frame{k+1}'],
dict(mode= 'immediate',
frame= dict( duration=0, redraw= True ),
transition=dict( duration=0)
)
],
#label='Date : {}'.format(date_range[k])
) for k in range(0,len(frames))],
transition= dict(duration=0),
x=0,
y=0,
currentvalue=dict(font=dict(size=12), visible=True, xanchor= 'center'),
len=1.0)
]
trace1 = go.Surface(x=xx1, y=xx2, z=fx, showscale=True, opacity=0.8)
trace2 = go.Scatter3d(x=None, y=None, z=None)
fig = go.Figure(data=[trace1, trace2], frames=frames)
#fig.add_surface(x=xx1, y=xx2, z=fx, showscale=True, opacity=0.9)
#fig.update_traces(contours_z=dict(show=True, usecolormap=True, highlightcolor="limegreen", project_z=True))
#fig.update(frames=frames)
fig.update_layout(updatemenus=[dict(type="buttons", buttons=[dict(label="Play", method="animate", args=[None, dict(fromcurrent=True)]), \
dict(label="Pause", method="animate", args=[[None], dict(fromcurrent=True, mode='immediate', transition= {'duration': 0}, frame=dict(redraw=True, duration=0))])])],
margin=dict(l=0, r=0, b=0, t=0), sliders=sliders)
fig.show()
class gradient_descent_2d_race(object):
def __init__(self, environ:str="jupyterlab"):
pio.renderers.default = environ # 'notebook' or 'colab' or 'jupyterlab'
self.wg_expr = widgets.Text(value="(sin(x1) - 2) ** 2 + (sin(x2) - 2) ** 2", description="Expression:")
self.wg_person_one = widgets.Text(value="(5, 5)", description="candidate 1:")
self.wg_person_two = widgets.Text(value="(5, 5)", description="candidate 2:")
self.button_compute = widgets.Button(description="Compute")
self.button_plot = widgets.Button(description="Plot")
self.compute_output = widgets.Output()
self.plot_output = widgets.Output()
self.button_box = widgets.HBox([self.button_compute, self.button_plot], description="operations")
self.config = widgets.VBox([self.wg_expr, self.wg_person_one, self.wg_person_two, self.button_box])
self.xn_list_p1, self.df_list_p1 = [], []
self.xn_list_p2, self.df_list_p2 = [], []
self.initialization()
def initialization(self):
display(self.config)
self.button_compute.on_click(self.compute)
display(self.compute_output)
self.button_plot.on_click(self.plot)
display(self.plot_output)
def compute(self, *args):
with self.compute_output:
# person_one
x0 = np.array(self.wg_person_one.value.split("(")[1].split(")")[0].split(","), dtype=float)
xn = x0
x1 = symbols("x1")
x2 = symbols("x2")
expr = sympify(self.wg_expr.value)
gradient = np.array([diff(expr, x1).subs(x1, xn[0]).subs(x2, xn[1]),
diff(expr, x2).subs(x1, xn[0]).subs(x2, xn[1])], dtype=float)
self.xn_list_p1.append(xn)
self.df_list_p1.append(gradient)
print("player one: x = [{}, {}]".format(xn[0], xn[1]))
print("player one: gradient= {}".format(gradient))
# person_two
x0 = np.array(self.wg_person_two.value.split("(")[1].split(")")[0].split(","), dtype=float)
xn = x0
x1 = symbols("x1")
x2 = symbols("x2")
expr = sympify(self.wg_expr.value)
gradient = np.array([diff(expr, x1).subs(x1, xn[0]).subs(x2, xn[1]),
diff(expr, x2).subs(x1, xn[0]).subs(x2, xn[1])], dtype=float)
self.xn_list_p2.append(xn)
self.df_list_p2.append(gradient)
print("player two: x = [{}, {}]".format(xn[0], xn[1]))
print("player two: gradient= {}".format(gradient))
clear_output(wait=True)
return None
def plot(self, *args):
with self.plot_output:
clear_output(wait=True)
#x0 = np.array(self.wg_x0.value.split(","), dtype=float)
x1 = symbols("x1")
x2 = symbols("x2")
expr = sympify(self.wg_expr.value)
xx1 = np.arange(np.array(self.xn_list_p1)[:, 0].min()*0.5, np.array(self.xn_list_p1)[:, 0].max()*1.5, 0.1)
xx2 = np.arange(np.array(self.xn_list_p1)[:, 1].min()*0.5, np.array(self.xn_list_p1)[:, 1].max()*1.5, 0.1)
xx1, xx2 = np.meshgrid(xx1, xx2)
f = lambdify((x1, x2), expr, "numpy")
fx = f(xx1, xx2)
f_xn_p1 = f(np.array(self.xn_list_p1)[:, 0], np.array(self.xn_list_p1)[:, 1])
f_xn_p2 = f(np.array(self.xn_list_p2)[:, 0], np.array(self.xn_list_p2)[:, 1])
frames, steps = [], []
for k in range(len(f_xn_p1)):
tmp_trace1 = go.Scatter3d(x=np.array(self.xn_list_p1)[:k,0], y=np.array(self.xn_list_p1)[:k,1], z=f_xn_p1)
tmp_trace2 = go.Scatter3d(x=np.array(self.xn_list_p2)[:k,0], y=np.array(self.xn_list_p2)[:k,1], z=f_xn_p2)
frame = go.Frame(dict(data=[tmp_trace1, tmp_trace2], name=f'frame{k+1}'), traces=[1, 2])
frames.append(frame)
step = dict(
method="update",
args=[{"visible": [True]},
{"title": "Slider switched to step: " + str(k+1)}], # layout attribute
)
steps.append(step)
sliders = [dict(steps= [dict(method= 'animate',
args= [[f'frame{k+1}'],
dict(mode= 'immediate',
frame= dict( duration=0, redraw= True ),
transition=dict( duration=0)
)
],
#label='Date : {}'.format(date_range[k])
) for k in range(0,len(frames))],
transition= dict(duration=0),
x=0,
y=0,
currentvalue=dict(font=dict(size=12), visible=True, xanchor= 'center'),
len=1.0)
]
trace1 = go.Surface(x=xx1, y=xx2, z=fx, showscale=True, opacity=0.4)
trace2 = go.Scatter3d(x=None, y=None, z=None)
trace3 = go.Scatter3d(x=None, y=None, z=None)
fig = go.Figure(data=[trace1, trace2, trace3], frames=frames)
fig.update_layout(updatemenus=[dict(type="buttons", buttons=[dict(label="Play", method="animate", args=[None, dict(fromcurrent=True)]), \
dict(label="Pause", method="animate", args=[[None], dict(fromcurrent=True, mode='immediate', transition= {'duration': 0}, frame=dict(redraw=True, duration=0))])])],
margin=dict(l=20, r=20, b=20, t=20), sliders=sliders)
fig.show()