429 lines
16 KiB
Python
429 lines
16 KiB
Python
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()
|