diff --git a/.gitignore b/.gitignore index fcb3202..58f9be0 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ htmlcov/ cover/ docs/_build build/ +dist/ diff --git a/descent/__init__.py b/descent/__init__.py index 1fe864c..5ee09ae 100644 --- a/descent/__init__.py +++ b/descent/__init__.py @@ -20,4 +20,4 @@ from .utils import * from .main import * -__version__ = '0.1.4' +__version__ = '0.1.5' diff --git a/descent/main.py b/descent/main.py index 8b97888..d63b79a 100644 --- a/descent/main.py +++ b/descent/main.py @@ -7,6 +7,7 @@ from collections import namedtuple, defaultdict from .utils import wrap, restruct, destruct import numpy as np +import tableprint as tp try: from time import perf_counter except ImportError: @@ -14,21 +15,23 @@ class Optimizer(object): - - def __init__(self, theta_init): + def __init__(self, theta_init, display=True): self.iteration = 0 self.theta = theta_init self.runtimes = list() self.store = defaultdict(list) + self.display = display def __next__(self): raise NotImplementedError - def run(self, maxiter=None): + def optional_print(self, message): + print(message, flush=True) if self.display else None + def run(self, maxiter=None): maxiter = np.inf if maxiter is None else (maxiter + self.iteration) - try: + self.optional_print(tp.header(['Iteration', 'Objective', 'Runtime'])) for k in count(start=self.iteration): self.iteration = k @@ -41,6 +44,11 @@ def run(self, maxiter=None): # TODO: run callbacks self.store['objective'].append(self.objective(destruct(self.theta))) + # Update display + self.optional_print(tp.row([self.iteration, + self.store['objective'][-1], + tp.humantime(self.runtimes[-1])])) + # TODO: check for convergence if k >= maxiter: break @@ -48,14 +56,23 @@ def run(self, maxiter=None): except KeyboardInterrupt: pass + # cleanup + self.optional_print(tp.hr(3)) + self.optional_print(u'\u279b Final objective: {}'.format(self.store['objective'][-1])) + self.optional_print(u'\u279b Total runtime: {}'.format(tp.humantime(sum(self.runtimes)))) + self.optional_print(u'\u279b Per iteration runtime: {} +/- {}'.format( + tp.humantime(np.mean(self.runtimes)), + tp.humantime(np.std(self.runtimes)), + )) + def restruct(self, x): return restruct(x, self.theta) @implements_iterator class GradientDescent(Optimizer): - - def __init__(self, theta_init, f_df, algorithm, options, proxop=None, rho=None): + def __init__(self, theta_init, f_df, algorithm, options=None, proxop=None, rho=None): + options = {} if options is None else options super().__init__(theta_init) self.objective, self.gradient = wrap(f_df, theta_init)