-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtabu_search.py
138 lines (131 loc) · 6.08 KB
/
tabu_search.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
from bin import Bin
from heuristics import BestFit, FirstFit, NextFit, WorstFit
from move_operators import Add, Change, Remove, Swap
import random
class TabuSearch:
heuristic_map = {
"f": FirstFit,
"n": NextFit,
"w": WorstFit,
"b": BestFit,
}
movers = [Add, Change, Remove, Swap]
def __init__(self, capacity, items, MAX_COMBINATION_LENGTH=10, MAX_ITERATIONS=5000, MAX_NO_CHANGE = 1000):
"""
Creates an instance that can run the tabu search algorithm.
:param capacity: The capacity of a bin.
:param items: The items that have to be packed in bins.
"""
self.MAX_COMBINATION_LENGTH = MAX_COMBINATION_LENGTH
self.MAX_ITERATIONS = MAX_ITERATIONS
self.MAX_NO_CHANGE = MAX_NO_CHANGE
self.bin_capacity = capacity
self.items = items
self.fitness = 0
self.bins = [Bin(capacity)]
self.tabu_list = set()
def run(self):
"""
Runs the tabu search algorithm and returns the results at the end of the process.
:return: (num_iterations, num_no_changes, chosen_combination)
"""
combination = "".join(
[random.choice(list(self.heuristic_map.keys())) for _ in range(random.randrange(self.MAX_COMBINATION_LENGTH) or 1)])
self.bins = self.generate_solution(combination)
self.fitness = sum(b.fitness() for b in self.bins) / len(self.bins)
self.tabu_list.add(combination)
current_iteration = 0
num_no_change = 0
while num_no_change < self.MAX_NO_CHANGE and current_iteration < self.MAX_ITERATIONS:
new_combination = self.apply_move_operator(combination)
while len(new_combination) > self.MAX_COMBINATION_LENGTH :
new_combination = self.apply_move_operator(new_combination)
if new_combination not in self.tabu_list:
self.tabu_list.add(new_combination)
solution = self.generate_solution(new_combination)
fitness = sum(b.fitness() for b in solution) / len(solution)
if fitness > self.fitness:
self.bins = solution
self.fitness = fitness
num_no_change = 0
combination = new_combination
current_iteration += 1
num_no_change += 1
return current_iteration, num_no_change, combination
def run2(self,AGsol):
"""
Runs the tabu search algorithm and returns the results at the end of the process.
:return: (num_iterations, num_no_changes, chosen_combination)
"""
combination = AGsol.best_solution.pattern
self.bins =self.generate_solution(combination)
self.fitness = AGsol.best_solution.fitness
self.tabu_list.add(combination)
current_iteration = 0
num_no_change = 0
while num_no_change < self.MAX_NO_CHANGE and current_iteration < self.MAX_ITERATIONS:
new_combination = self.apply_move_operator(combination)
while len(new_combination) > self.MAX_COMBINATION_LENGTH :
new_combination = self.apply_move_operator(new_combination)
if new_combination not in self.tabu_list:
self.tabu_list.add(new_combination)
solution = self.generate_solution(new_combination)
fitness = sum(b.fitness() for b in solution) / len(solution)
if fitness > self.fitness:
self.bins = solution
self.fitness = fitness
num_no_change = 0
combination = new_combination
current_iteration += 1
else :
current_iteration += 1
num_no_change += 1
return current_iteration, num_no_change, combination
def run3(self,chromosome):
"""
Runs the tabu search algorithm and returns the results at the end of the process.
:return: (num_iterations, num_no_changes, chosen_combination)
"""
combination = chromosome.pattern
self.bins =self.generate_solution(combination)
self.fitness = chromosome.fitness
self.tabu_list.add(combination)
current_iteration = 0
num_no_change = 0
while num_no_change < self.MAX_NO_CHANGE and current_iteration < self.MAX_ITERATIONS:
new_combination = self.apply_move_operator(combination)
while len(new_combination) > self.MAX_COMBINATION_LENGTH :
new_combination = self.apply_move_operator(new_combination)
if new_combination not in self.tabu_list:
self.tabu_list.add(new_combination)
solution = self.generate_solution(new_combination)
fitness = sum(b.fitness() for b in solution) / len(solution)
if fitness > self.fitness:
self.bins = solution
self.fitness = fitness
num_no_change = 0
combination = new_combination
current_iteration += 1
else :
current_iteration += 1
num_no_change += 1
return current_iteration, num_no_change, combination
def generate_solution(self, pattern):
"""
Generates a candidate solution based on the pattern given.
:param pattern: A pattern indicating the order in which heuristics need to be applied to get the solution.
:return: A list of bins to serve as a solution.
"""
solution = [Bin(self.bin_capacity)]
pattern_length = len(pattern)
for idx, item in enumerate(self.items):
h = pattern[idx % pattern_length]
solution = self.heuristic_map[h].apply(item, solution)
return solution
def apply_move_operator(self, pattern):
"""
Applies a random move operator to the given pattern.
:param pattern: The pattern to apply the move operator to.
:return: The pattern after the move operator has been applied.
"""
return random.choice(self.movers).apply(pattern, list(self.heuristic_map.keys()))