Files
cmdla/project/plotting/L-BFGS.py

429 lines
16 KiB
Python
Raw Normal View History

2024-07-30 14:43:25 +02:00
from config import (
LBFGS_RESULTS,
PLOTS_PATH,
log_tick_formatter,
nanoseconds_to_seconds,
setup
)
from scipy.interpolate import griddata
from mpl_toolkits.mplot3d import Axes3D
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import ticker, cm
import seaborn as sns
from scipy.interpolate import interp1d
metrics_map = {
'error': ['relative', 'residual'],
'time': ['meantime'],
'iterations': ['iterations']
}
def plot_lambda_epsilon_3D(kind):
if kind not in ['error', 'time', 'iterations']:
raise ValueError("kind must be one of 'error', 'time', 'iterations'")
df = pd.read_csv(LBFGS_RESULTS.format("3D", "statisticsLBFGS-lambda-eps-m300n20--{}.csv".format(kind)))
metrics = metrics_map[kind]
for metric in metrics:
lambda_values = df['lambda']
epsilon_values = df['epsilon']
meantime_values = df[metric]
lambda_mesh, epsilon_mesh = np.meshgrid(np.unique(lambda_values), np.unique(epsilon_values))
# Cubic interpolation of the data to make it smooth
meantime_interp = griddata((lambda_values, epsilon_values), meantime_values, (lambda_mesh, epsilon_mesh),
method='cubic')
# Plot the 3D surface
fig = plt.figure(figsize=(13, 9))
ax = fig.add_subplot(111, projection='3d')
sns.set(style="white", context="paper")
surface = ax.plot_surface(np.log10(lambda_mesh), np.log10(epsilon_mesh), meantime_interp, edgecolor='none',
cmap='viridis', alpha=0.9)
fig.colorbar(surface, ax=ax, shrink=0.5, aspect=5, pad=0.2)
# Set labels
ax.set_xlabel('λ', labelpad=20, fontsize=15)
ax.set_ylabel('ϵ', labelpad=20, fontsize=15)
if metric == 'meantime':
metric = 'running time'
ax.set_zlabel('time (s)', labelpad=10, fontsize=20)
ax.zaxis.set_major_formatter(ticker.FuncFormatter(nanoseconds_to_seconds))
title = 'L-BFGS ' + metric + ' with respect to λ and ϵ over 10000 runs'
elif metric in ['relative', 'residual']:
if metric == 'relative':
metric = 'relative error'
ax.set_zlabel(metric, labelpad=10, fontsize=20)
title = 'L-BFGS ' + metric + ' with respect to λ and ϵ'
else:
ax.set_zlabel(metric, labelpad=10, fontsize=20)
title = 'L-BFGS ' + metric + ' with respect to λ and ϵ'
plt.title(title, fontsize=20, pad=20, fontweight='bold', loc='center')
# Set logarithmic locator of ticks
ax.xaxis.set_major_formatter(ticker.FuncFormatter(log_tick_formatter))
ax.yaxis.set_major_formatter(ticker.FuncFormatter(log_tick_formatter))
ax.xaxis.set_tick_params(size=15)
ax.yaxis.set_tick_params(size=15)
ax.zaxis.set_tick_params(size=15)
metric = metric.replace(" ", "_")
plt.savefig(PLOTS_PATH.format("LBFGS/3D", "LBFGS-lambda-eps--{}.png".format(metric)))
plt.show()
def plot_epsilon_memory_3D(kind):
if kind not in ['error', 'iterations']:
raise ValueError("kind must be one of 'error', 'iterations'")
df = pd.read_csv(LBFGS_RESULTS.format("3D", "statisticsLBFGS-eps-mem-m300n20--{}.csv".format(kind)))
metrics = metrics_map[kind]
for metric in metrics:
memory_values = df['memsize']
epsilon_values = df['epsilon']
meantime_values = df[metric]
memory_mesh, epsilon_mesh = np.meshgrid(np.unique(memory_values), np.unique(epsilon_values))
# Cubic interpolation of the data to make it smooth
meantime_interp = griddata((memory_values, epsilon_values), meantime_values, (memory_mesh, epsilon_mesh),
method='cubic')
# Plot the 3D surface
fig = plt.figure(figsize=(13, 9))
ax = fig.add_subplot(111, projection='3d')
sns.set(style="white")
sns.set_context('paper')
surface = ax.plot_surface(memory_mesh, np.log10(epsilon_mesh), meantime_interp, edgecolor='none',
cmap='viridis', alpha=0.9)
fig.colorbar(surface, ax=ax, shrink=0.5, aspect=5, pad=0.2)
# Set labels
ax.set_xlabel('memory size', labelpad=20, fontsize=15)
ax.set_ylabel('ϵ', labelpad=20, fontsize=15)
ax.set_zlabel('iterations', labelpad=10, fontsize=20)
if metric in ['relative', 'residual']:
if metric == 'relative':
metric = 'relative error'
ax.set_zlabel(metric, labelpad=10, fontsize=20)
plt.title('L-BFGS ' + metric + ' with respect to the memory size and ϵ', fontsize=20, pad=20,
fontweight='bold',
loc='center')
ax.yaxis.set_major_formatter(ticker.FuncFormatter(log_tick_formatter))
ax.xaxis.set_tick_params(size=15)
ax.yaxis.set_tick_params(size=15)
ax.zaxis.set_tick_params(size=15)
plt.savefig(PLOTS_PATH.format("LBFGS/3D", "LBFGS-eps-mem--{}.png".format(metric)))
plt.show()
def plot_iterations_error_2D(conditioning="well"):
if conditioning not in ["well", "ill"]:
raise ValueError("conditioning must be one of 'well' or 'ill'")
setup(
title=f'L-BFGS errors with respect to iterations on {conditioning}-conditioned matrix',
x_label='Iterations',
y_label='Error'
)
conditioning = conditioning + "_conditioned"
df = pd.read_csv(LBFGS_RESULTS.format(conditioning, "statisticsLBFGS-iterations-m1000n20--error.csv"))
maxiterations_values = df['maxiterations']
relative_values = df['relative']
residual_values = df['residual']
# since from a certain row the residual and the relative gap are the same as the previous row, we can all the
# next rows and keep only the first row of each value
relative_values = relative_values.drop_duplicates(keep='first')
residual_values = residual_values.drop_duplicates(keep='first')
assert len(relative_values) == len(residual_values)
maxiterations_values = maxiterations_values[:len(relative_values)]
max_iter_interp = np.linspace(min(maxiterations_values), max(maxiterations_values), 100)
relative_interp = interp1d(maxiterations_values, relative_values, kind='cubic')(max_iter_interp)
residual_interp = interp1d(maxiterations_values, residual_values, kind='cubic')(max_iter_interp)
data = pd.DataFrame({
'maxiterations': max_iter_interp,
'relative': relative_interp,
'residual': residual_interp
})
sns.lineplot(data=data, x='maxiterations', y='relative', label='Relative Error', color='blue')
sns.lineplot(data=data, x='maxiterations', y='residual', label='Residual', color='red')
plt.legend(loc='upper right', fontsize=15)
plt.ylim(0, None)
conditioning = "LBFGS/" + conditioning
plt.savefig(PLOTS_PATH.format(conditioning, "LBFGS-iterations-error.png"))
plt.show()
def plot_norm_gradient_2D(conditioning="well", smoothing_window=5):
if conditioning not in ["well", "ill"]:
raise ValueError("conditioning must be one of 'well' or 'ill'")
cond = conditioning
setup(
title=f'L-BFGS convergence on {cond}-conditioned matrix',
x_label='Iterations',
y_label=' '
)
conditioning = conditioning + "_conditioned"
df = pd.read_csv(LBFGS_RESULTS.format(conditioning, "statisticsLBFGS-iterations-m1000n20--error-norm.csv"))
gradnorm = df['mean_gradient'].unique()
error = df['mean_relative'].unique()
residual = df['mean_residual'].unique()
iterations = list(range(len(gradnorm)))
assert len(error) == len(residual) == len(gradnorm)
iterations_interp = np.linspace(min(iterations), max(iterations), len(residual))
gradnorm = interp1d(iterations, gradnorm, kind='quadratic')(iterations_interp)
error = interp1d(iterations, error, kind='quadratic')(iterations_interp)
residual = interp1d(iterations, residual, kind='quadratic')(iterations_interp)
smoothed_gradnorm = np.convolve(gradnorm, np.ones(smoothing_window) / smoothing_window, mode='valid')
smoothed_error = np.convolve(error, np.ones(smoothing_window) / smoothing_window, mode='valid')
smoothed_residual = np.convolve(residual, np.ones(smoothing_window) / smoothing_window, mode='valid')
data = pd.DataFrame({
'iterations': iterations[:len(smoothed_gradnorm)],
'Gradient Norm': smoothed_gradnorm,
'Relative Error': smoothed_error,
'Residual': smoothed_residual
})
sns.lineplot(data=data, x='iterations', y='Gradient Norm', label='Gradient Norm', color='green')
sns.lineplot(data=data, x='iterations', y='Relative Error', label='Relative Error', color='blue')
sns.lineplot(data=data, x='iterations', y='Residual', label='Residual', color='red')
plt.yscale('log')
plt.legend(loc='upper right', fontsize=15)
conditioning = "LBFGS/" + conditioning
plt.savefig(PLOTS_PATH.format(conditioning, f"LBFGS-iterations-gradient-{cond}.png"))
plt.show()
def plot_iteration_memory(conditioning="well", smoothing_window=3):
if conditioning not in ["well", "ill"]:
raise ValueError("conditioning must be one of 'well' or 'ill'")
cond = conditioning
setup(title=f'L-BFGS relative error for different memory sizes on {cond}-conditioned matrix', x_label='Iterations', y_label='Relative Error')
conditioning = conditioning + "_conditioned"
df = pd.read_csv(
LBFGS_RESULTS.format(conditioning, "statisticsLBFGS-iterations-m1000n20--memsize.csv"),
)
unique_memsize = df['memsize'].unique()
for memsize in unique_memsize:
data = df[df['memsize'] == memsize]
data.reset_index(drop=True, inplace=True)
gradients_indices = data.index[data['gradient'].drop_duplicates(keep='first').index]
relative_errors = data.loc[gradients_indices]['relative'].values
extension = np.full(smoothing_window, relative_errors[-1])
relative_errors = np.concatenate((relative_errors, extension))
print(len(relative_errors))
iterations = list(range(len(relative_errors)))
iterations_interp = np.linspace(min(iterations), max(iterations), len(relative_errors))
relative_interp = interp1d(iterations, relative_errors, kind='cubic')(iterations_interp)
smoothed_relative = np.convolve(relative_interp, np.ones(smoothing_window) / smoothing_window, mode='valid')
data = pd.DataFrame({
'iterations': iterations_interp[:len(smoothed_relative)],
'relative': smoothed_relative
})
sns.lineplot(data=data, x='iterations', y='relative', label='Memory Size = {}'.format(memsize))
plt.legend(loc='upper right', fontsize=15)
plt.yscale('log')
conditioning = "LBFGS/" + conditioning
plt.savefig(PLOTS_PATH.format(conditioning, f"LBFGS-iterations-memory-{cond}.png"))
plt.show()
def plot_awls_vs_exact_gradient_decrease_2D(smoothing_window=3):
conditioning = "well_conditioned"
df2_awls_quad = pd.read_csv(
LBFGS_RESULTS.format(conditioning, "statisticsLBFGS-AWLS-quad-iterations-m1000n20--error-norm.csv"),
)
df3_exact = pd.read_csv(
LBFGS_RESULTS.format(conditioning, "statisticsLBFGS-iterations-m1000n20--error-norm.csv"),
)
setup(title='L-BFGS ExactLS vs AWLS gradient norm on well-conditioned matrix', x_label='Iterations', y_label='Gradient Norm')
gradnorm_awls_quad = df2_awls_quad['mean_gradient'].unique()
gradnorm_exact = df3_exact['mean_gradient'].unique()
extension = np.full(len(gradnorm_awls_quad) - len(gradnorm_exact) + smoothing_window, gradnorm_exact[-1])
gradnorm_exact = np.concatenate((gradnorm_exact, extension))
extension = np.full(smoothing_window, gradnorm_awls_quad[-1])
gradnorm_awls_quad = np.concatenate((gradnorm_awls_quad, extension))
assert len(gradnorm_awls_quad) == len(gradnorm_exact)
iterations = list(range(len(gradnorm_awls_quad)))
iterations_interp = np.linspace(min(iterations), max(iterations), len(gradnorm_awls_quad))
gradnorm_awls_quad = interp1d(iterations, gradnorm_awls_quad, kind='cubic')(iterations_interp)
gradnorm_exact = interp1d(iterations, gradnorm_exact, kind='cubic')(iterations_interp)
smoothed_gradnorm_awls_quad = np.convolve(gradnorm_awls_quad, np.ones(smoothing_window) / smoothing_window,
mode='valid')
smoothed_gradnorm_exact = np.convolve(gradnorm_exact, np.ones(smoothing_window) / smoothing_window, mode='valid')
data = pd.DataFrame({
'iterations': iterations_interp[:len(smoothed_gradnorm_awls_quad)],
'AWLS quadratic': smoothed_gradnorm_awls_quad,
'ExactLS': smoothed_gradnorm_exact
})
sns.lineplot(data=data, x='iterations', y='AWLS quadratic', label='AWLS', color='blue')
sns.lineplot(data=data, x='iterations', y='ExactLS', label='ExactLS', color='green')
plt.yscale('log')
plt.legend(loc='upper right', fontsize=15)
conditioning = "LBFGS/" + conditioning
plt.savefig(PLOTS_PATH.format(conditioning, "LBFGS-LS-gradient-comparison.png"))
plt.show()
def BFGS_comparison():
folder = "comparison_BFGS"
df_BFGS = pd.read_csv(
LBFGS_RESULTS.format(folder, "statisticsBFGS-iterations-m1000n20--error-norm.csv"),
)
df_LBFGS = pd.read_csv(
LBFGS_RESULTS.format("well_conditioned", "statisticsLBFGS-iterations-m1000n20--error-norm.csv"),
)
setup(title='BFGS vs L-BFGS gradient norm on well-conditioned matrix', x_label='Iterations', y_label=' ')
gradnorm_BFGS = df_BFGS['mean_gradient'][::2]
gradnorm_LBFGS = df_LBFGS['mean_gradient'][::2]
error_BFGS = df_BFGS['mean_relative'][::2]
error_LBFGS = df_LBFGS['mean_relative'][::2]
residual_BFGS = df_BFGS['mean_residual'][::2]
residual_LBFGS = df_LBFGS['mean_residual'][::2]
min_len = 27
iterations = list(range(min_len))
gradnorm_BFGS = gradnorm_BFGS[:min_len]
gradnorm_LBFGS = gradnorm_LBFGS[:min_len]
error_BFGS = error_BFGS[:min_len]
error_LBFGS = error_LBFGS[:min_len]
residual_BFGS = residual_BFGS[:min_len]
residual_LBFGS = residual_LBFGS[:min_len]
iterations_interp = np.linspace(min(iterations), max(iterations), len(residual_BFGS))
gradnorm_BFGS = interp1d(iterations, gradnorm_BFGS, kind='cubic')(iterations_interp)
gradnorm_LBFGS = interp1d(iterations, gradnorm_LBFGS, kind='cubic')(iterations_interp)
error_BFGS = interp1d(iterations, error_BFGS, kind='cubic')(iterations_interp)
error_LBFGS = interp1d(iterations, error_LBFGS, kind='cubic')(iterations_interp)
residual_BFGS = interp1d(iterations, residual_BFGS, kind='cubic')(iterations_interp)
residual_LBFGS = interp1d(iterations, residual_LBFGS, kind='cubic')(iterations_interp)
data = pd.DataFrame({
'Iterations': iterations_interp,
'BFGS Gradient': gradnorm_BFGS,
'LBFGS Gradient': gradnorm_LBFGS,
'BFGS Error': error_BFGS,
'LBFGS Error': error_LBFGS,
'BFGS Residual': residual_BFGS,
'LBFGS Residual': residual_LBFGS
})
data_long = pd.melt(data, id_vars=['Iterations'], var_name='algorithm', value_name='value')
# Define custom color palette
custom_palette = {
'BFGS Gradient': 'red',
'BFGS Error': 'orange',
'BFGS Residual': 'gold',
'LBFGS Gradient': 'blue',
'LBFGS Error': 'green',
'LBFGS Residual': 'deepskyblue'
}
# Create the plot
sns.lineplot(data=data_long, x='Iterations', y='value', hue='algorithm', palette=custom_palette)
plt.yscale('log')
handles, labels = plt.gca().get_legend_handles_labels()
labels, handles = zip(*sorted(zip(labels, handles), key=lambda t: t[0]))
plt.legend(handles, labels, loc='upper right', fontsize=15)
conditioning = "LBFGS/" + folder
plt.savefig(PLOTS_PATH.format(conditioning, "BFGS-LBFGS-gradient-comparison.png"))
plt.show()
if __name__ == "__main__":
# plot all 3D plots
"""
plot_lambda_epsilon_3D('error')
plot_lambda_epsilon_3D('time')
plot_lambda_epsilon_3D('iterations')
plot_epsilon_memory_3D('error')
plot_epsilon_memory_3D('iterations')
"""
plot_norm_gradient_2D("well")
plot_norm_gradient_2D("ill")
# plot_awls_vs_exact_gradient_decrease_2D()
# plot_iteration_memory("well")
plot_iteration_memory("ill")
BFGS_comparison()