Planned maintenance on Wednesday, 2021-01-20, 17:00-18:00. Expect some interruptions during that time

Commit 6f4449c8 authored by theos's avatar theos

First steps towards VL-LBFGS

parent 38acac27
......@@ -46,6 +46,7 @@ class LineSearch(object, Loggable):
self.pk = None
self.f_k = None
self.f_k_minus_1 = None
self.fprime_k = None
def set_functions(self, f, fprime, f_args=()):
......@@ -56,7 +57,8 @@ class LineSearch(object, Loggable):
self.fprime = fprime
self.f_args = f_args
def set_coordinates(self, xk, pk, f_k=None, fprime_k=None):
def _set_coordinates(self, xk, pk, f_k=None, fprime_k=None,
f_k_minus_1=None):
"""
Set the coordinates for a new line search.
......@@ -89,6 +91,8 @@ class LineSearch(object, Loggable):
else:
self.fprime_k = fprime_k
self.f_k_minus_1 = f_k_minus_1
def _phi(self, alpha):
if alpha == 0:
value = self.f_k
......@@ -105,5 +109,6 @@ class LineSearch(object, Loggable):
return bare_dot(gradient, self.pk)
@abc.abstractmethod
def perform_line_search(self):
def perform_line_search(self, xk, pk, f_k=None, fprime_k=None,
f_k_minus_1=None):
raise NotImplementedError
......@@ -43,42 +43,15 @@ class LineSearchStrongWolfe(LineSearch):
self.max_step_size = max_step_size
self.max_iterations = int(max_iterations)
self.max_zoom_iterations = int(max_zoom_iterations)
self.f_k_minus_1 = None
self._last_alpha_star = 1.
def set_coordinates(self, xk, pk, f_k=None, fprime_k=None,
f_k_minus_1=None):
"""
Set the coordinates for a new line search.
Parameters
----------
xk : ndarray, d2o, field
Starting point.
pk : ndarray, d2o, field
Unit vector in search direction.
f_k : float (optional)
Function value f(x_k).
fprime_k : ndarray, d2o, field (optional)
Function value fprime(xk).
f_k_minus_1 : None, float (optional)
Function value f(x_k-1).
"""
super(LineSearchStrongWolfe, self).set_coordinates(xk=xk,
pk=pk,
f_k=None,
fprime_k=None)
self.f_k_minus_1 = f_k_minus_1
def perform_line_search(self):
def perform_line_search(self, xk, pk, f_k=None, fprime_k=None,
f_k_minus_1=None):
self._set_coordinates(xk=xk,
pk=pk,
f_k=f_k,
fprime_k=fprime_k,
f_k_minus_1=f_k_minus_1)
c1 = self.c1
c2 = self.c2
max_step_size = self.max_step_size
......
......@@ -84,18 +84,17 @@ class QuasiNewtonMinimizer(object, Loggable):
convergence = self.convergence_level+2
break
descend_direction = self._get_descend_direction(gradient,
gradient_norm)
descend_direction = self._get_descend_direction(x, gradient)
# compute the step length, which minimizes f_k along the
# search direction = the gradient
self.line_searcher.set_coordinates(xk=x,
step_length, f_k = self.line_searcher.perform_line_search(
xk=x,
pk=descend_direction,
f_k=f_k,
fprime_k=gradient,
f_k_minus_1=f_k_minus_1)
f_k_minus_1 = f_k
step_length, f_k = self.line_searcher.perform_line_search()
# update x
x += descend_direction*step_length
......
......@@ -4,5 +4,5 @@ from .quasi_newton_minimizer import QuasiNewtonMinimizer
class SteepestDescent(QuasiNewtonMinimizer):
def _get_descend_direction(self, gradient, gradient_norm):
return gradient/(-gradient_norm)
def _get_descend_direction(self, x, gradient):
return gradient/(-gradient.dot(gradient))
# -*- coding: utf-8 -*-
from .quasi_newton_minimizer import QuasiNewtonMinimizer
class VL_BFGS(QuasiNewtonMinimizer):
def _get_descend_direction(self, x, gradient):
pass
class InformationStore(object):
def __init__(self, history_length, x0, gradient):
self.history_length = history_length
self.s = LimitedList(history_length)
self.y = LimitedList(history_length)
self.last_x = x0
self.last_gradient = gradient
self.k = 0
self.dot_matrix = {}
@property
def actual_history_length(self):
return min(self.k, self.history_length)
def add_new_point(self, x, gradient):
self.k += 1
new_s = x - self.last_x
self.s.add(new_s)
new_y = gradient - self.last_gradient
self.y.add(new_y)
k = self.k
m = self.actual_history_length
big_m = self.history_length
# compute dot products
for i in xrange(k-1, k-m-1, -1):
# new_s with s
key = (big_m+m, big_m+1+i)
self.dot_matrix[key] = new_s.dot(self.s[i])
# new_s with y
key = (big_m+m, i+1)
self.dot_matrix[key] = new_s.dot(self.y[i])
# new_y with s
if i != k-1:
key = (big_m+1+i, k)
self.dot_matrix[key] = new_y.dot(self.s[i])
# new_y with y
# actually key = (i+1, k) but the convention is that the first
# index is larger than the second one
key = (k, i+1)
self.dot_matrix[key] = new_y.dot(self.y[i])
# gradient with s
key = (big_m+1+i, 0)
self.dot_matrix[key] = gradient.dot(self.s[i])
# gradient with y
key = (i+1, 0)
self.dot_matrix[key] = gradient.dot(self.y[i])
# gradient with gradient
key = (0, 0)
self.dot_matrix[key] = gradient.dot(gradient)
self.last_x = x
self.last_gradient = gradient
# TODO: remove old entries from dictionary
class LimitedList(object):
def __init__(self, history_length):
self.history_length = int(history_length)
self._offset = 0
self._storage = []
def __getitem__(self, index):
return self._storage[index-self._offset]
def add(self, value):
if len(self._storage) == self.history_length:
self._storage.pop(0)
self._offset += 1
self._storage.append(value)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment