-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathassignment2.py
More file actions
337 lines (269 loc) · 11.1 KB
/
assignment2.py
File metadata and controls
337 lines (269 loc) · 11.1 KB
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
from search import *
from random import randint
from assignment2aux import *
import numpy as np
def read_tiles_from_file(filename):
# Task 1
# Return a tile board constructed using a configuration in a file.
# Store tile board
tile_map = list()
f = open(filename, 'r')
while f:
line = f.readline()
if not line:
break
temp_list = list()
for ele in line:
match ele:
case " ":
temp_list.append(tuple(()))
case "i":
temp_list.append(tuple((0,)))
case "L":
temp_list.append(tuple((0, 1)))
case "I":
temp_list.append(tuple((0, 2)))
case "T":
temp_list.append(tuple((0, 1, 2)))
tile_map.append(tuple(temp_list))
f.close()
return tuple(tile_map)
class KNetWalk(Problem):
def __init__(self, tiles):
if type(tiles) is str:
self.tiles = read_tiles_from_file(tiles)
else:
self.tiles = tiles
height = len(self.tiles)
width = len(self.tiles[0])
# max fitness: number of when all tile can connect with their all neighbor
self.max_fitness = sum(sum(len(tile) for tile in row) for row in self.tiles)
super().__init__(self.generate_random_state())
def generate_random_state(self):
height = len(self.tiles)
width = len(self.tiles[0])
return [randint(0, 3) for _ in range(height) for _ in range(width)]
def actions(self, state):
height = len(self.tiles)
width = len(self.tiles[0])
# For every tile, three possible orientation (except current orientation)
# were added to action list
return [(i, j, k) for i in range(height) for j in range(width) for k in [0, 1, 2, 3] if
state[i * width + j] != k]
def result(self, state, action):
pos = action[0] * len(self.tiles[0]) + action[1]
return state[:pos] + [action[2]] + state[pos + 1:]
def goal_test(self, state):
return self.value(state) == self.max_fitness
def value(self, state):
# Task 2
# Return an integer fitness value of a given state.
dx = [0, -1, 0, 1]
dy = [1, 0, -1, 0]
# In a correct connection, 0 connect with 2, 1 connect with 3,
# 2 connect with 0, 3 connect with 1
correct_conn = [2, 3, 0, 1]
height = len(self.tiles)
width = len(self.tiles[0])
# fitness of current state
current_fitness = 0
# Check all tile
for i in range(height):
for j in range(width):
if type(state) is not list:
break
# Get real orientation from state and self.tiles
current_ori = state[i * width + j]
true_ori = [(ele + current_ori) % 4 for ele in self.tiles[i][j]]
# Check 4 direction
for dir in range(4):
xx = i + dx[dir]
yy = j + dy[dir]
# If this direction exist
if dir in true_ori and 0 <= xx < height and 0 <= yy < width:
# Get the real orientation of neighbor in this direction
neighbor_ori = state[xx * width + yy]
true_neighbor_ori = tuple((con + neighbor_ori) % 4 for con in self.tiles[xx][yy])
# true_neighbor_ori = [(ele + neighbor_ori) % 4 for ele in self.tiles[xx][yy]]
# Update fitness if there is a correct connection
if correct_conn[dir] in true_neighbor_ori:
current_fitness += 1
return current_fitness
# Task 3
# Configure an exponential schedule for simulated annealing.
sa_schedule = exp_schedule(k=75, lam=0.08, limit=200)
# Task 4
# Configure parameters for the genetic algorithm.
pop_size = 15
num_gen = 1000
mutation_prob = 0.1
def local_beam_search(problem, population):
# Task 5
# Implement local beam search.
# Return a goal state if found in the population.
# Return the fittest state in the population if the next population contains no fitter state.
# Replace the line below with your code.
# get beam width
beam_width = len(population)
# store population in priority queue
# sort population by their value
curr_q = []
for peo in population:
curr_q.append((problem.value(peo), peo))
curr_q.sort(reverse=True)
while True:
temp_q = []
# get all possible child state and store in temp_q
for item in curr_q:
actions = problem.actions(item[1])
for act in actions:
child = problem.result(item[1], act)
temp_q.append((problem.value(child), child))
temp_q.sort(reverse=True)
# if reach goal fitness, return that state
if temp_q[0][0] == problem.max_fitness:
return temp_q[0][1]
# if no better instance, return the best one
elif temp_q[0][0] < curr_q[0][0]:
return temp_q[0][1]
# else select some best children as next population, number is beam_width
else:
curr_q.clear()
for every_child in range(min(beam_width, len(temp_q))):
curr_q.append(temp_q[every_child])
def stochastic_beam_search(problem, population, limit=1000):
# Task 6
# Implement stochastic beam search.
# Return a goal state if found in the population.
# Return the fittest state in the population if the generation limit is reached.
# Replace the line below with your code.
beam_width = len(population)
curr_q = []
for peo in population:
curr_q.append((problem.value(peo), peo))
curr_q.sort(reverse=True)
for i in range(limit):
temp_q = []
# get all possible child state and store in temp_q
for item in curr_q:
if not isinstance(item[1], list):
continue
actions = problem.actions(item[1])
# print(actions)
for act in actions:
child = problem.result(item[1], act)
# print(child)
temp_q.append((problem.value(child), child))
if not temp_q:
break
temp_q.sort(reverse=True)
# if best child reach goal fitness, finish search
if temp_q[0][0] == problem.max_fitness:
return temp_q[0][1]
# else find the best child of number beam width as next population
else:
# calculate probability of every child
min_value = temp_q[-1][0]
value_diff = [(child[0] - min_value) ** 2 for child in temp_q]
diff_sum = sum(value_diff)
temp_q_array = np.array([child[1] for child in temp_q])
# if all child has same fitness
if diff_sum != 0:
child_prob = [prob / diff_sum for prob in value_diff]
select_index = list(np.random.choice([a for a in range(len(temp_q_array))],
min(beam_width, len(temp_q_array)),
False, child_prob))
# if all child has no same fitness
else:
select_index = list(np.random.choice([a for a in range(len(temp_q_array))],
min(beam_width, len(temp_q_array)),
False))
# take child from temp_q as next population
curr_q = [temp_q[idx] for idx in select_index]
# return the best state after search
return curr_q[0][1]
if __name__ == '__main__':
# Task 1 test code
# network = KNetWalk('assignment2config.txt')
# visualise(network.tiles, network.initial)
# Task 2 test code
# run = 0
# method = 'hill climbing'
# while True:
# network = KNetWalk('assignment2config.txt')
# state = hill_climbing(network)
# if network.goal_test(state):
# break
# else:
# print(f'{method} run {run}: no solution found')
# print(f'best state fitness {network.value(state)} out of {network.max_fitness}')
# visualise(network.tiles, state)
# run += 1
# print(f'{method} run {run}: solution found')
# visualise(network.tiles, state)
# Task 3 test code
# run = 0
# method = 'simulated annealing'
# while True:
# network = KNetWalk('assignment2config.txt')
# state = simulated_annealing(network, schedule=sa_schedule)
# if network.goal_test(state):
# break
# else:
# print(f'{method} run {run}: no solution found')
# print(f'best state fitness {network.value(state)} out of {network.max_fitness}')
# visualise(network.tiles, state)
# run += 1
# print(f'{method} run {run}: solution found')
# visualise(network.tiles, state)
# Task 4 test code
# run = 0
# method = 'genetic algorithm'
# while True:
# network = KNetWalk('assignment2config.txt')
# height = len(network.tiles)
# width = len(network.tiles[0])
# state = genetic_algorithm([network.generate_random_state() for _ in range(pop_size)], network.value, [0, 1, 2, 3], network.max_fitness, num_gen, mutation_prob)
# if network.goal_test(state):
# break
# else:
# print(f'{method} run {run}: no solution found')
# print(f'best state fitness {network.value(state)} out of {network.max_fitness}')
# visualise(network.tiles, state)
# run += 1
# print(f'{method} run {run}: solution found')
# visualise(network.tiles, state)
# Task 5 test code
# run = 0
# method = 'local beam search'
# while True:
# network = KNetWalk('assignment2config.txt')
# height = len(network.tiles)
# width = len(network.tiles[0])
# state = local_beam_search(network, [network.generate_random_state() for _ in range(100)])
# if network.goal_test(state):
# break
# else:
# print(f'{method} run {run}: no solution found')
# print(f'best state fitness {network.value(state)} out of {network.max_fitness}')
# visualise(network.tiles, state)
# run += 1
# print(f'{method} run {run}: solution found')
# visualise(network.tiles, state)
# Task 6 test code
# run = 0
# method = 'stochastic beam search'
# while True:
# network = KNetWalk('assignment2config.txt')
# height = len(network.tiles)
# width = len(network.tiles[0])
# state = stochastic_beam_search(network, [network.generate_random_state() for _ in range(100)])
# if network.goal_test(state):
# break
# else:
# print(f'{method} run {run}: no solution found')
# print(f'best state fitness {network.value(state)} out of {network.max_fitness}')
# visualise(network.tiles, state)
# run += 1
# print(f'{method} run {run}: solution found')
# visualise(network.tiles, state)