-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmetropolis.py
72 lines (63 loc) · 2.4 KB
/
metropolis.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
import numpy as np
import math as m
from collections import defaultdict
from board import Board
def run_metropolis(
N,
max_moves,
beta_init,
mode="solve",
beta_strategy="fixed",
strategy_params=None,
count_params=None,
debug=True,
):
if mode not in ["solve", "count"]:
raise NotImplementedError("mode not implemented.")
board = Board(N)
board.init_board()
beta = beta_init
accepted_moves = 0
conflict_dict = defaultdict(int)
# If the number of conflict is already 0, we're done.
if mode=="solve" and board.nb_conflicts == 0:
debug and board.print_board()
return 0, board.queens, dict(conflict_dict)
for j in range(max_moves):
# Update beta according to the strategy
if beta_strategy == "fixed":
pass
elif beta_strategy == "annealing_quantized":
if j % strategy_params["iterations_step"] == 0:
beta *= strategy_params["annealing_factor"]
else:
raise NotImplementedError("beta_strategy not implemented.")
# Debugging information
if debug and j % 500 == 0:
print(
f"Move {j} - beta: {beta} - nb_conflicts: {board.nb_conflicts} - accepted_moves: {accepted_moves}"
)
# Choose a random swap and compute the energy difference
qn1 = np.random.randint(N)
qn2 = np.random.randint(N)
de, _, _ = board.var_move(qn1, qn2)
# Metropolis algorithm: If de <= 0, accept the move.
# Otherwise, accept the move with probability given by e^(-de * beta)
# (so, bigger de => less likely to accept move).
# If we accept the move, swap queens
# and update the total system energy.
p = np.random.random()
if (de <= 0) or (
p < m.exp(-de * beta) and (accepted_moves := accepted_moves + 1)
):
board.move_queen(qn1, qn2)
# If the number of conflict is 0, we're done.
if mode == "solve" and board.nb_conflicts == 0:
if debug and board.N <= 100:
board.print_board()
break
# If we're in count mode, we keep track of the number of conflicts
# for each move.
if mode == "count" and j >= count_params["convergence_moves"]:
conflict_dict[board.nb_conflicts] += 1
return j, board.queens, dict(conflict_dict)