2
2
Hidden Markov Model Evolver
3
3
4
4
Usage:
5
- fsm_evolve .py [-h] [--generations GENERATIONS] [--population POPULATION]
5
+ hmm_evolve .py [-h] [--generations GENERATIONS] [--population POPULATION]
6
6
[--mu MUTATION_RATE] [--bottleneck BOTTLENECK] [--processes PROCESSORS]
7
7
[--output OUTPUT_FILE] [--objective OBJECTIVE] [--repetitions REPETITIONS]
8
8
[--turns TURNS] [--noise NOISE] [--nmoran NMORAN]
9
- [--states NUM_STATES]
9
+ [--states NUM_STATES] [--algorithm ALGORITHM]
10
10
11
11
Options:
12
12
-h --help Show help
22
22
--noise NOISE Match noise [default: 0.00]
23
23
--nmoran NMORAN Moran Population Size, if Moran objective [default: 4]
24
24
--states NUM_STATES Number of FSM states [default: 5]
25
+ --algorithm ALGORITHM Which algorithm to use (EA for evolutionary algorithm or PS for
26
+ particle swarm algorithm) [default: EA]
25
27
"""
26
28
27
- #####
28
- # This is a potential candidate for PSO optimization, which will require
29
- # combining the matrices.
30
- #####
31
-
32
- import random
33
- from random import randrange , choice
34
-
35
29
from docopt import docopt
36
- import numpy as np
37
-
38
- from axelrod import Action
39
- from axelrod .strategies .hmm import HMMPlayer
40
- from axelrod_dojo import Params , Population , prepare_objective
41
-
42
- C , D = Action .C , Action .D
43
-
44
-
45
- def copy_lists (rows ):
46
- new_rows = list (map (list , rows ))
47
- return new_rows
48
-
49
- def random_vector (size ):
50
- """Create a random vector of values in [0, 1] that sums to 1."""
51
- vector = []
52
- s = 1
53
- for _ in range (size - 1 ):
54
- r = s * random .random ()
55
- vector .append (r )
56
- s -= r
57
- vector .append (s )
58
- return vector
59
-
60
- def normalize_vector (vec ):
61
- s = sum (vec )
62
- vec = [v / s for v in vec ]
63
- return vec
64
-
65
- def mutate_row (row , mutation_rate ):
66
- randoms = np .random .random (len (row ))
67
- for i in range (len (row )):
68
- if randoms [i ] < mutation_rate :
69
- ep = random .uniform (- 1 , 1 ) / 4
70
- row [i ] += ep
71
- if row [i ] < 0 :
72
- row [i ] = 0
73
- if row [i ] > 1 :
74
- row [i ] = 1
75
- return row
76
-
77
-
78
- class HMMParams (Params ):
79
-
80
- def __init__ (self , num_states , mutation_rate = None , transitions_C = None ,
81
- transitions_D = None , emission_probabilities = None ,
82
- initial_state = 0 , initial_action = C ):
83
- self .PlayerClass = HMMPlayer
84
- self .num_states = num_states
85
- if mutation_rate is None :
86
- self .mutation_rate = 10 / (num_states ** 2 )
87
- else :
88
- self .mutation_rate = mutation_rate
89
- if transitions_C is None :
90
- self .randomize ()
91
- else :
92
- # Make sure to copy the lists
93
- self .transitions_C = copy_lists (transitions_C )
94
- self .transitions_D = copy_lists (transitions_D )
95
- self .emission_probabilities = list (emission_probabilities )
96
- self .initial_state = initial_state
97
- self .initial_action = initial_action
98
-
99
30
100
- def player (self ):
101
- player = self .PlayerClass (self .transitions_C , self .transitions_D ,
102
- self .emission_probabilities ,
103
- self .initial_state , self .initial_action )
104
- return player
105
-
106
- def copy (self ):
107
- return HMMParams (self .num_states , self .mutation_rate ,
108
- self .transitions_C , self .transitions_D ,
109
- self .emission_probabilities ,
110
- self .initial_state , self .initial_action )
111
-
112
- @staticmethod
113
- def random_params (num_states ):
114
- t_C = []
115
- t_D = []
116
- for _ in range (num_states ):
117
- t_C .append (random_vector (num_states ))
118
- t_D .append (random_vector (num_states ))
119
- initial_state = randrange (num_states )
120
- # initial_action = choice([C, D])
121
- initial_action = C
122
- return t_C , t_D , initial_state , initial_action
123
-
124
- def randomize (self ):
125
- t_C , t_D , initial_state , initial_action = self .random_params (self .num_states )
126
- self .emission_probabilities = [random .random () for _ in range (self .num_states )]
127
- self .transitions_C = t_C
128
- self .transitions_D = t_D
129
- self .initial_state = initial_state
130
- self .initial_action = initial_action
131
-
132
- @staticmethod
133
- def mutate_rows (rows , mutation_rate ):
134
- for i , row in enumerate (rows ):
135
- row = mutate_row (row , mutation_rate )
136
- rows [i ] = normalize_vector (row )
137
- return rows
138
-
139
- def mutate (self ):
140
- self .transitions_C = self .mutate_rows (
141
- self .transitions_C , self .mutation_rate )
142
- self .transitions_D = self .mutate_rows (
143
- self .transitions_D , self .mutation_rate )
144
- self .emission_probabilities = mutate_row (
145
- self .emission_probabilities , self .mutation_rate )
146
- if random .random () < self .mutation_rate / 10 :
147
- self .initial_action = self .initial_action .flip ()
148
- if random .random () < self .mutation_rate / (10 * self .num_states ):
149
- self .initial_state = randrange (self .num_states )
150
- # Change node size?
151
-
152
- @staticmethod
153
- def crossover_rows (rows1 , rows2 ):
154
- num_states = len (rows1 )
155
- crosspoint = randrange (num_states )
156
- new_rows = copy_lists (rows1 [:crosspoint ])
157
- new_rows += copy_lists (rows2 [crosspoint :])
158
- return new_rows
159
-
160
- @staticmethod
161
- def crossover_weights (w1 , w2 ):
162
- crosspoint = random .randrange (len (w1 ))
163
- new_weights = list (w1 [:crosspoint ]) + list (w2 [crosspoint :])
164
- return new_weights
165
-
166
- def crossover (self , other ):
167
- # Assuming that the number of states is the same
168
- t_C = self .crossover_rows (self .transitions_C , other .transitions_C )
169
- t_D = self .crossover_rows (self .transitions_D , other .transitions_D )
170
- emissions = self .crossover_weights (
171
- self .emission_probabilities , other .emission_probabilities )
172
- return HMMParams (self .num_states , self .mutation_rate ,
173
- t_C , t_D , emissions ,
174
- self .initial_state , self .initial_action )
175
-
176
- @staticmethod
177
- def repr_rows (rows ):
178
- ss = []
179
- for row in rows :
180
- ss .append ("_" .join (list (map (str , row ))))
181
- return "|" .join (ss )
182
-
183
- def __repr__ (self ):
184
- return "{}:{}:{}:{}:{}" .format (
185
- self .initial_state ,
186
- self .initial_action ,
187
- self .repr_rows (self .transitions_C ),
188
- self .repr_rows (self .transitions_D ),
189
- self .repr_rows ([self .emission_probabilities ])
190
- )
191
-
192
- @classmethod
193
- def parse_repr (cls , s ):
194
- def parse_matrix (sm ):
195
- rows = []
196
- lines = sm .split ('|' )
197
- for line in lines :
198
- row = line .split ('_' )
199
- row = list (map (float , row ))
200
- rows .append (row )
201
- return row
202
- lines = s .split (':' )
203
- initial_state = int (lines [0 ])
204
- initial_action = lines [1 ]
205
- t_C = parse_matrix (lines [2 ])
206
- t_D = parse_matrix (lines [3 ])
207
- ps = parse_matrix (lines [4 ])
208
- return cls (t_C , t_D , ps , initial_state , initial_action )
31
+ from axelrod_dojo import HMMParams , Population , prepare_objective
32
+ from axelrod_dojo .algorithms .particle_swarm_optimization import PSO
209
33
210
34
211
35
if __name__ == '__main__' :
@@ -227,13 +51,33 @@ def parse_matrix(sm):
227
51
noise = float (arguments ['--noise' ])
228
52
nmoran = int (arguments ['--nmoran' ])
229
53
230
- # FSM
54
+ # HMM
231
55
num_states = int (arguments ['--states' ])
232
- param_kwargs = {"num_states" : num_states }
233
-
234
- objective = prepare_objective (name , turns , noise , repetitions , nmoran )
235
- population = Population (HMMParams , param_kwargs , population , objective ,
236
- output_filename , bottleneck ,
237
- mutation_probability ,
238
- processes = processes )
239
- population .run (generations )
56
+ params_kwargs = {"num_states" : num_states }
57
+
58
+ if arguments ['--algorithm' ] == "PS" :
59
+ objective = prepare_objective (name , turns , noise , repetitions , nmoran )
60
+ pso = PSO (HMMParams , params_kwargs , objective = objective ,
61
+ population = population , generations = generations ,
62
+ size = num_states )
63
+
64
+ xopt_helper , fopt = pso .swarm ()
65
+ xopt = HMMParams (num_states = num_states )
66
+ xopt .read_vector (xopt_helper , num_states )
67
+ else :
68
+ objective = prepare_objective (name , turns , noise , repetitions , nmoran )
69
+ population = Population (HMMParams , params_kwargs , population , objective ,
70
+ output_filename , bottleneck , mutation_probability ,
71
+ processes = processes )
72
+ population .run (generations )
73
+
74
+ # Get the best member of the population to output.
75
+ scores = population .score_all ()
76
+ record , record_holder = 0 , - 1
77
+ for i , s in enumerate (scores ):
78
+ if s >= record :
79
+ record = s
80
+ record_holder = i
81
+ xopt , fopt = population .population [record_holder ], record
82
+
83
+ print ("Best Score: {} {}" .format (fopt , xopt ))
0 commit comments