-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathhamming_reactive_tabu_search.py
152 lines (141 loc) · 6.03 KB
/
hamming_reactive_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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
'''
References
[1] R. Battiti and G. Tecchiolli, “The Reactive Tabu Search,” ORSA J. Comput., vol. 6, no. 2, pp. 126–140, 1994, doi: 10.1287/ijoc.6.2.126.
[2] R. Battiti and M. Protasi, “Reactive Search, a history-based heuristic for MAX-SAT,” ACM J. Exp. Algorithmics, vol. 2, pp. 130–157, 1997, doi: 10.1145/264216.264220.
'''
from base_solver import Base_Solver
import numpy as np
import random
import time
from itertools import chain
class H_RTS(Base_Solver):
def __init__(self, input_cnf_file, verbose):
super(H_RTS, self).__init__(input_cnf_file, verbose)
'''
Initialize tabu list and its length
Note that tabu list is a circular list
'''
self.tabu_list = []
self.Tf = 0.1
self.tabu_tenure = 0
def initialize_tabu(self, tabu_tenure):
self.tabu_list = []
self.tabu_tenure = tabu_tenure
def add_tabu(self, literal):
'''
Add a move to tabu list
'''
if len(self.tabu_list) < self.tabu_tenure:
self.tabu_list.append(abs(literal))
else: # tabu list is full
self.tabu_list.pop(0)
self.tabu_list.append(literal)
def hamming_distance(self,a, b):
c = np.bitwise_xor(a, b)
n = c.sum()
return n
def react(self, X_f, X_i):
deriv = float(self.hamming_distance(X_f, X_i) / (self.tabu_tenure+1)) -1
if deriv <= 0:
self.Tf += 0.01
elif deriv > 0.5:
self.Tf -= 0.01
if self.Tf > 0.25:
self.Tf = 0.25
elif self.Tf < 0.025:
self.Tf = 0.025
return max(int(self.Tf*self.nvars), 4)
def pick_all_lits(self,id_unsat_clauses, tabu_list=None):
all_allowed_lits = []
for ind in id_unsat_clauses:
all_allowed_lits += self.list_clauses[ind]
if tabu_list is not None:
all_allowed_lits = list(set(all_allowed_lits)^set(tabu_list))
else:
all_allowed_lits = list(set(all_allowed_lits))
return all_allowed_lits
def solve(self):
initial = time.time()
self.initialize_pool()
while self.nb_tries < self.MAX_TRIES and not self.is_sat:
self.generate()
self.initialize_cost()
self.Tf = 0.1
self.tabu_tenure = int(self.Tf * self.nvars)
'''
TODO: NOB_LS here
'''
while self.nb_flips < self.MAX_FLIPS and not self.check():
'''
[Local Search]
- GSAT idea (Intensification => focus on best var)
- Among all variables that occur in unsat clauses
- Choose a variable x which minimizes cost to flip
- Compute cost when flipping each literal
- Cost = break - make
- After flipping, check if the nb of UNSAT clause decreases or not
- Yes => continuer
- No => stop at local optimum
'''
improved = True
while improved and self.nb_flips < self.MAX_FLIPS and not self.check():
all_allowed_lits = self.pick_all_lits(self.id_unsat_clauses)
break_make_count = []
for literal in all_allowed_lits:
break_make_count.append(self.evaluate_breakcount(literal, bs=1, ms=1))
nb_unsat = len(self.id_unsat_clauses)
if nb_unsat + min(break_make_count) < nb_unsat:
x = all_allowed_lits[np.argmin(break_make_count)]
self.flip(x)
else:
improved = False
X_i = self.assignment.copy()
'''
[Reactive Tabu Search]
- Initialize tabu list with defined tabu tenure
- Compute new X after 2(T+1) iterations with TS
+ Remove all tabus in list of candidates
+ If all vars are tabu => ignore tabu and take initial list of variables
- Update tabu tenure
'''
it = 0
while not self.check() and it < 2*(self.tabu_tenure+1):
# compute allowed literals wrt tabu list
all_allowed_lits = self.pick_all_lits(self.id_unsat_clauses, self.tabu_list)
if len(all_allowed_lits) == 0: # else take all_allowed_lits and ignore tabu
all_allowed_lits = self.pick_all_lits(self.id_unsat_clauses)
break_make_count = []
for literal in all_allowed_lits:
break_make_count.append(self.evaluate_breakcount(literal, bs=1, ms=1))
x = all_allowed_lits[np.argmin(break_make_count)]
# '''
# Random walk
# '''
# if self.random_walk:
# p = random.random()
# if p < self.noise_parameter: # pick x randomly from literals in all unsat clause
# x = random.choice(all_allowed_lits)
# else:
# x = all_allowed_lits[np.argmin(break_count)]
# else:
# x = all_allowed_lits[np.argmin(break_count)]
self.flip(x)
self.add_tabu(x)
it += 1
X_f = self.assignment.copy()
'''
Update tabu tenure based on search history
'''
self.tabu_tenure = self.react(X_f, X_i)
if self.check():
self.is_sat = True
end = time.time()
print('Nb flips: {0} '.format(self.nb_flips))
print('Nb tries: {0} '.format(self.nb_tries))
print('CPU time: {0:10.4f} s '.format(end-initial))
if self.is_sat:
print('SAT')
return self.assignment
else:
print('UNKNOWN')
return None