Commit bbd2040f authored by Martin Reinecke's avatar Martin Reinecke
Browse files

merge NewtonCG change

parent 2005e30f
Pipeline #61059 passed with stages
in 9 minutes and 25 seconds
...@@ -18,8 +18,14 @@ ...@@ -18,8 +18,14 @@
import numpy as np import numpy as np
from ..logger import logger from ..logger import logger
from ..probing import approximation2endo
from ..sugar import makeOp
from .conjugate_gradient import ConjugateGradient
from .iteration_controllers import (AbsDeltaEnergyController,
GradientNormController)
from .line_search import LineSearch from .line_search import LineSearch
from .minimizer import Minimizer from .minimizer import Minimizer
from .quadratic_energy import QuadraticEnergy
class DescentMinimizer(Minimizer): class DescentMinimizer(Minimizer):
...@@ -79,7 +85,8 @@ class DescentMinimizer(Minimizer): ...@@ -79,7 +85,8 @@ class DescentMinimizer(Minimizer):
# compute a step length that reduces energy.value sufficiently # compute a step length that reduces energy.value sufficiently
new_energy, success = self.line_searcher.perform_line_search( new_energy, success = self.line_searcher.perform_line_search(
energy=energy, pk=self.get_descent_direction(energy), energy=energy,
pk=self.get_descent_direction(energy, f_k_minus_1),
f_k_minus_1=f_k_minus_1) f_k_minus_1=f_k_minus_1)
if not success: if not success:
self.reset() self.reset()
...@@ -103,7 +110,7 @@ class DescentMinimizer(Minimizer): ...@@ -103,7 +110,7 @@ class DescentMinimizer(Minimizer):
def reset(self): def reset(self):
pass pass
def get_descent_direction(self, energy): def get_descent_direction(self, energy, old_value=None):
"""Calculates the next descent direction. """Calculates the next descent direction.
Parameters Parameters
...@@ -112,6 +119,10 @@ class DescentMinimizer(Minimizer): ...@@ -112,6 +119,10 @@ class DescentMinimizer(Minimizer):
An instance of the Energy class which shall be minimized. The An instance of the Energy class which shall be minimized. The
position of `energy` is used as the starting point of minimization. position of `energy` is used as the starting point of minimization.
old_value : float
if provided, this must be the value of the energy in the previous
step.
Returns Returns
------- -------
Field Field
...@@ -127,7 +138,7 @@ class SteepestDescent(DescentMinimizer): ...@@ -127,7 +138,7 @@ class SteepestDescent(DescentMinimizer):
functional's gradient for minimization. functional's gradient for minimization.
""" """
def get_descent_direction(self, energy): def get_descent_direction(self, energy, _=None):
return -energy.gradient return -energy.gradient
...@@ -144,7 +155,7 @@ class RelaxedNewton(DescentMinimizer): ...@@ -144,7 +155,7 @@ class RelaxedNewton(DescentMinimizer):
super(RelaxedNewton, self).__init__(controller=controller, super(RelaxedNewton, self).__init__(controller=controller,
line_searcher=line_searcher) line_searcher=line_searcher)
def get_descent_direction(self, energy): def get_descent_direction(self, energy, _=None):
return -energy.metric.inverse_times(energy.gradient) return -energy.metric.inverse_times(energy.gradient)
...@@ -154,44 +165,31 @@ class NewtonCG(DescentMinimizer): ...@@ -154,44 +165,31 @@ class NewtonCG(DescentMinimizer):
Algorithm derived from SciPy sources. Algorithm derived from SciPy sources.
""" """
def __init__(self, controller, line_searcher=None): def __init__(self, controller, napprox=0, line_searcher=None, name=None,
nreset=20):
if line_searcher is None: if line_searcher is None:
line_searcher = LineSearch(preferred_initial_step_size=1.) line_searcher = LineSearch(preferred_initial_step_size=1.)
super(NewtonCG, self).__init__(controller=controller, super(NewtonCG, self).__init__(controller=controller,
line_searcher=line_searcher) line_searcher=line_searcher)
self._napprox = napprox
def get_descent_direction(self, energy): self._name = name
float64eps = np.finfo(np.float64).eps self._nreset = nreset
grad = energy.gradient
maggrad = abs(grad).sum() def get_descent_direction(self, energy, old_value=None):
termcond = np.min([0.5, np.sqrt(maggrad)]) * maggrad if old_value is None:
xsupi = energy.position*0 ic = GradientNormController(iteration_limit=5)
ri = grad else:
psupi = -ri alpha = 0.1
dri0 = ri.vdot(ri) ediff = alpha*(old_value-energy.value)
ic = AbsDeltaEnergyController(
i = 0 ediff, iteration_limit=200, name=self._name)
while True: e = QuadraticEnergy(0*energy.position, energy.metric, energy.gradient)
if abs(ri).sum() <= termcond: p = None
return xsupi if self._napprox > 1:
Ap = energy.apply_metric(psupi) unscmet, sc = energy.unscaled_metric()
# check curvature p = makeOp(approximation2endo(unscmet, self._napprox)*sc).inverse
curv = psupi.vdot(Ap) e, conv = ConjugateGradient(ic, nreset=self._nreset)(e, p)
if 0 <= curv <= 3*float64eps: return -e.position
return xsupi
elif curv < 0:
return xsupi if i > 0 else (dri0/curv) * grad
alphai = dri0/curv
xsupi = xsupi + alphai*psupi
ri = ri + alphai*Ap
dri1 = ri.vdot(ri)
psupi = (dri1/dri0)*psupi - ri
i += 1
dri0 = dri1 # update numpy.dot(ri,ri) for next time.
# curvature keeps increasing, bail out
raise ValueError("Warning: CG iterations didn't converge. "
"The Hessian is not positive definite.")
class L_BFGS(DescentMinimizer): class L_BFGS(DescentMinimizer):
...@@ -210,7 +208,7 @@ class L_BFGS(DescentMinimizer): ...@@ -210,7 +208,7 @@ class L_BFGS(DescentMinimizer):
self._s = [None]*self.max_history_length self._s = [None]*self.max_history_length
self._y = [None]*self.max_history_length self._y = [None]*self.max_history_length
def get_descent_direction(self, energy): def get_descent_direction(self, energy, _=None):
x = energy.position x = energy.position
s = self._s s = self._s
y = self._y y = self._y
...@@ -273,7 +271,7 @@ class VL_BFGS(DescentMinimizer): ...@@ -273,7 +271,7 @@ class VL_BFGS(DescentMinimizer):
def reset(self): def reset(self):
self._information_store = None self._information_store = None
def get_descent_direction(self, energy): def get_descent_direction(self, energy, _=None):
x = energy.position x = energy.position
gradient = energy.gradient gradient = energy.gradient
# initialize the information store if it doesn't already exist # initialize the information store if it doesn't already exist
......
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