-
-
Notifications
You must be signed in to change notification settings - Fork 47.1k
Added implementation for simulated annealing #1679
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
6bb353d
added hill climbing algorithm
1ca5de2
Shorten long lines, streamline get_neighbors()
cclauss 565bc6e
Update hill_climbing.py
cclauss 6791179
Update and rename optimization/hill_climbing.py to searches/hill_clim…
cclauss c28cb38
added hill climbing algorithm
21a65d4
Shorten long lines, streamline get_neighbors()
cclauss f576e27
Update hill_climbing.py
cclauss 6054fd8
Rebased
b633a30
merged with remote
16b6d06
added simulated annealing.py
170672d
added final comments and test
f5b767f
black formatted
7caad29
restricted search domain
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
# https://en.wikipedia.org/wiki/Simulated_annealing | ||
import math, random | ||
from hill_climbing import SearchProblem | ||
|
||
|
||
def simulated_annealing( | ||
search_prob, | ||
find_max: bool = True, | ||
max_x: float = math.inf, | ||
min_x: float = -math.inf, | ||
max_y: float = math.inf, | ||
min_y: float = -math.inf, | ||
visualization: bool = False, | ||
start_temperate: float = 100, | ||
rate_of_decrease: float = 0.01, | ||
threshold_temp: float = 1, | ||
) -> SearchProblem: | ||
""" | ||
implementation of the simulated annealing algorithm. We start with a given state, find | ||
all its neighbors. Pick a random neighbor, if that neighbor improves the solution, we move | ||
in that direction, if that neighbor does not improve the solution, we generate a random | ||
real number between 0 and 1, if the number is within a certain range (calculated using | ||
temperature) we move in that direction, else we pick another neighbor randomly and repeat the process. | ||
Args: | ||
search_prob: The search state at the start. | ||
find_max: If True, the algorithm should find the minimum else the minimum. | ||
max_x, min_x, max_y, min_y: the maximum and minimum bounds of x and y. | ||
visualization: If True, a matplotlib graph is displayed. | ||
start_temperate: the initial temperate of the system when the program starts. | ||
rate_of_decrease: the rate at which the temperate decreases in each iteration. | ||
threshold_temp: the threshold temperature below which we end the search | ||
Returns a search state having the maximum (or minimum) score. | ||
""" | ||
search_end = False | ||
current_state = search_prob | ||
current_temp = start_temperate | ||
scores = [] | ||
iterations = 0 | ||
best_state = None | ||
|
||
while not search_end: | ||
current_score = current_state.score() | ||
if best_state is None or current_score > best_state.score(): | ||
best_state = current_state | ||
scores.append(current_score) | ||
iterations += 1 | ||
next_state = None | ||
neighbors = current_state.get_neighbors() | ||
while ( | ||
next_state is None and neighbors | ||
): # till we do not find a neighbor that we can move to | ||
index = random.randint(0, len(neighbors) - 1) # picking a random neighbor | ||
picked_neighbor = neighbors.pop(index) | ||
change = picked_neighbor.score() - current_score | ||
|
||
if ( | ||
picked_neighbor.x > max_x | ||
or picked_neighbor.x < min_x | ||
or picked_neighbor.y > max_y | ||
or picked_neighbor.y < min_y | ||
): | ||
continue # neighbor outside our bounds | ||
|
||
if not find_max: | ||
change = change * -1 # incase we are finding minimum | ||
if change > 0: # improves the solution | ||
next_state = picked_neighbor | ||
else: | ||
probabililty = (math.e) ** ( | ||
change / current_temp | ||
) # probability generation function | ||
if random.random() < probabililty: # random number within probability | ||
next_state = picked_neighbor | ||
current_temp = current_temp - (current_temp * rate_of_decrease) | ||
|
||
if ( | ||
current_temp < threshold_temp or next_state is None | ||
): # temperature below threshold, or | ||
# couldnt find a suitaable neighbor | ||
search_end = True | ||
else: | ||
current_state = next_state | ||
|
||
if visualization: | ||
import matplotlib.pyplot as plt | ||
|
||
plt.plot(range(iterations), scores) | ||
plt.xlabel("Iterations") | ||
plt.ylabel("Function values") | ||
plt.show() | ||
return best_state | ||
|
||
|
||
if __name__ == "__main__": | ||
|
||
def test_f1(x, y): | ||
return (x ** 2) + (y ** 2) | ||
|
||
# starting the problem with initial coordinates (12, 47) | ||
prob = SearchProblem(x=12, y=47, step_size=1, function_to_optimize=test_f1) | ||
local_min = simulated_annealing( | ||
prob, find_max=False, max_x=100, min_x=5, max_y=50, min_y=-5, visualization=True | ||
) | ||
print( | ||
"The minimum score for f(x, y) = x^2 + y^2 with the domain 100 > x > 5 " | ||
f"and 50 > y > - 5 found via hill climbing: {local_min.score()}" | ||
) | ||
|
||
# starting the problem with initial coordinates (12, 47) | ||
prob = SearchProblem(x=12, y=47, step_size=1, function_to_optimize=test_f1) | ||
local_min = simulated_annealing( | ||
prob, find_max=True, max_x=100, min_x=5, max_y=50, min_y=-5, visualization=True | ||
) | ||
print( | ||
"The maximum score for f(x, y) = x^2 + y^2 with the domain 100 > x > 5 " | ||
f"and 50 > y > - 5 found via hill climbing: {local_min.score()}" | ||
) | ||
|
||
def test_f2(x, y): | ||
return (3 * x ** 2) - (6 * y) | ||
|
||
prob = SearchProblem(x=3, y=4, step_size=1, function_to_optimize=test_f1) | ||
local_min = simulated_annealing(prob, find_max=False, visualization=True) | ||
print( | ||
"The minimum score for f(x, y) = 3*x^2 - 6*y found via hill climbing: " | ||
f"{local_min.score()}" | ||
) | ||
|
||
prob = SearchProblem(x=3, y=4, step_size=1, function_to_optimize=test_f1) | ||
local_min = simulated_annealing(prob, find_max=True, visualization=True) | ||
print( | ||
"The maximum score for f(x, y) = 3*x^2 - 6*y found via hill climbing: " | ||
f"{local_min.score()}" | ||
) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please insert some doctests here as discussed in CONTRIBUTING.md.