diff --git a/code/python/src/algorithms/random_walk/algorithms.py b/code/python/src/algorithms/random_walk/algorithms.py index 001b5c4..7a8ed45 100644 --- a/code/python/src/algorithms/random_walk/algorithms.py +++ b/code/python/src/algorithms/random_walk/algorithms.py @@ -32,14 +32,50 @@ __all__ = [ def adaptive_walk_algorithm( landscape: Landscape, r: float, + coords_init: tuple, optimise: EnumOptimiseMode, verbose: bool, ): ''' Führt den Adapative-Walk Algorithmus aus, um ein lokales Minimum zu bestimmen. ''' - log_warn('Noch nicht implementiert!'); - return; + + # lege Fitness- und Umgebungsfunktionen fest: + match optimise: + case EnumOptimiseMode.max: + f = lambda x: -landscape.fitness(*x); + case _: + f = lambda x: landscape.fitness(*x); + nbhd = lambda x: landscape.neighbourhood(*x, r=r, strict=True); + label = lambda x: landscape.label(*x); + + # initialisiere + x = coords_init; + fx = f(x); + fy = fx; + N = nbhd(x); + + # führe walk aus: + while True: + # Wähle zufälligen Punkt und berechne fitness-Wert: + y = uniform_random_choice(N); + fy = f(y); + + # Nur dann aktualisieren, wenn sich f-Wert verbessert: + if fy < fx: + # Punkt + Umgebung + f-Wert aktualisieren + x = y; + fx = fy; + N = nbhd(x); + else: + # Nichts machen! + pass; + + # Nur dann (erfolgreich) abbrechen, wenn f-Wert lokal Min: + if fx <= min([f(y) for y in N], default=fx): + break; + + return x; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # METHOD gradient walk @@ -48,14 +84,56 @@ def adaptive_walk_algorithm( def gradient_walk_algorithm( landscape: Landscape, r: float, + coords_init: tuple, optimise: EnumOptimiseMode, verbose: bool, ): ''' Führt den Gradient-Descent (bzw. Ascent) Algorithmus aus, um ein lokales Minimum zu bestimmen. ''' - log_warn('Noch nicht implementiert!'); - return; + + # lege Fitness- und Umgebungsfunktionen fest: + match optimise: + case EnumOptimiseMode.max: + f = lambda x: -landscape.fitness(*x); + case _: + f = lambda x: landscape.fitness(*x); + nbhd = lambda x: landscape.neighbourhood(*x, r=r, strict=True); + label = lambda x: landscape.label(*x); + + # initialisiere + x = coords_init; + fx = landscape.fitness(*x); + fy = fx; + N = nbhd(x); + f_values = [f(y) for y in N]; + fmin = min(f_values); + Z = [y for y, fy in zip(N, f_values) if fy == fmin]; + + # führe walk aus: + while True: + # Wähle zufälligen Punkt mit steilstem Abstieg und berechne fitness-Wert: + y = uniform_random_choice(Z); + fy = fmin; + + # Nur dann aktualisieren, wenn sich f-Wert verbessert: + if fy < fx: + # Punkt + Umgebung + f-Wert aktualisieren + x = y; + fx = fy; + N = nbhd(y); + f_values = [f(y) for y in N]; + fmin = min(f_values); + Z = [y for y, fy in zip(N, f_values) if fy == fmin]; + else: + # Nichts machen! + pass; + + # Nur dann (erfolgreich) abbrechen, wenn f-Wert lokal Min: + if fx <= min([f(y) for y in N], default=fx): + break; + + return x; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # METHOD metropolis walk @@ -64,6 +142,8 @@ def gradient_walk_algorithm( def metropolis_walk_algorithm( landscape: Landscape, r: float, + coords_init: tuple, + T: float, annealing: bool, optimise: EnumOptimiseMode, verbose: bool, @@ -71,5 +151,56 @@ def metropolis_walk_algorithm( ''' Führt den Metropolis-Walk Algorithmus aus, um ein lokales Minimum zu bestimmen. ''' - log_warn('Noch nicht implementiert!'); - return; + + # lege Fitness- und Umgebungsfunktionen fest: + match optimise: + case EnumOptimiseMode.max: + f = lambda x: -landscape.fitness(*x); + case _: + f = lambda x: landscape.fitness(*x); + nbhd = lambda x: landscape.neighbourhood(*x, r=r, strict=True); + label = lambda x: landscape.label(*x); + + # initialisiere + x = coords_init; + fx = f(x); + fy = fx; + nbhd_x = nbhd(x); + + # führe walk aus: + k = 0; + while True: + # Wähle zufälligen Punkt und berechne fitness-Wert: + y = uniform_random_choice(nbhd_x); + r = uniform(0,1); + fy = f(y); + + # Nur dann aktualisieren, wenn sich f-Wert verbessert: + if fy < fx or r < math.exp(-(fy-fx)/T): + # Punkt + Umgebung + f-Wert aktualisieren + x = y; + fx = fy; + nbhd_x = nbhd(x); + else: + # Nichts machen! + pass; + + # »Temperatur« ggf. abkühlen: + if annealing: + T = cool_temperature(T, k); + + # Nur dann (erfolgreich) abbrechen, wenn f-Wert lokal Min: + if fx <= min([f(y) for y in nbhd_x], default=fx): + break; + + k += 1; + + return x; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# AUXILIARY METHODS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +def cool_temperature(T: float, k: int, const: float = 1.) -> float: + harm = const*(k + 1); + return T/(1 + T/harm);