1 | # experiment met een genetisch algoritme voor de kanalenplanning
|
---|
2 | # het genetische zit er nu nauwelijks in, en wat er in zit is ongetwijfeld
|
---|
3 | # fout. wat er wel goed in zit is het parseren van de bestaande situatie en
|
---|
4 | # het scoren van (nu random) configuraties.
|
---|
5 |
|
---|
6 | # de score functie is nu redelijk simplistisch:
|
---|
7 | # - de score van een combinatie van twee interfaces op een node is 1 als er
|
---|
8 | # twee of meer kanalen tussen zitten, en -1 als dat niet zo is
|
---|
9 | # - de score van een node is de som van de scores van alle combinaties van
|
---|
10 | # interfaces van die node, maal het aantal interfaces. dat laatste omdat
|
---|
11 | # een positieve score hebben knapper is met meer interfaces dan met minder,
|
---|
12 | # dus dat moet beloond. een negatieve score is erger met meer interfaces
|
---|
13 | # (belangrijker node, waarschijnlijk) dan met minder, dus dat moet
|
---|
14 | # bestraft.
|
---|
15 | # - de totale score is de som van de scores van de nodes
|
---|
16 | #
|
---|
17 | # het stukje "genetisch" aan het einde is afgeraffeld. mutation en
|
---|
18 | # recombination gebeuren op de oplossing zelf ipv op een bitstring
|
---|
19 | # representatie. selection is dom en neemt gewoon de helft beste oplossingen.
|
---|
20 | import re
|
---|
21 | import random
|
---|
22 | import os
|
---|
23 |
|
---|
24 | # deze twee zijn nodig voor de evaluatie van een configuratie
|
---|
25 | nodes = {} # nodename -> Node
|
---|
26 | groups = {} # essid -> WIGroup
|
---|
27 |
|
---|
28 | population = []
|
---|
29 |
|
---|
30 | # gegeven een lijst, geef de lijst zonder dubbele elementen
|
---|
31 | def uniq(l):
|
---|
32 | d = {}
|
---|
33 | for e in l:
|
---|
34 | d[e] = 1
|
---|
35 | return d.keys()
|
---|
36 |
|
---|
37 | def multiget(d, ks):
|
---|
38 | return map(lambda k: d[k], ks)
|
---|
39 |
|
---|
40 | class Node:
|
---|
41 | def __init__(self, name, wis):
|
---|
42 | self.name = name
|
---|
43 | self.wis = wis
|
---|
44 | for wi in wis:
|
---|
45 | wi.setNode(self)
|
---|
46 | def wi_byname(self, name):
|
---|
47 | wi = filter(lambda wi: wi.name == name, self.wis)
|
---|
48 | assert len(wi) == 1
|
---|
49 | return wi[0]
|
---|
50 | def score(self):
|
---|
51 | score = 0
|
---|
52 | for i in range(len(self.wis)):
|
---|
53 | wi_i = self.wis[i]
|
---|
54 | if wi_i.group == None:
|
---|
55 | # orphaned interface
|
---|
56 | continue
|
---|
57 | if wi_i.group.channel == None:
|
---|
58 | print "huh?"
|
---|
59 | for j in range(len(self.wis)):
|
---|
60 | if i != j:
|
---|
61 | wi_j = self.wis[j]
|
---|
62 | if wi_j.group == None:
|
---|
63 | continue
|
---|
64 | diff = abs(wi_i.group.channel - wi_j.group.channel)
|
---|
65 | if diff > 2:
|
---|
66 | score = score + 1
|
---|
67 | else:
|
---|
68 | score = score - 1
|
---|
69 | score = score * len(self.wis)
|
---|
70 | return score
|
---|
71 |
|
---|
72 | class WI:
|
---|
73 | def __init__(self, name):
|
---|
74 | self.name = name
|
---|
75 | self.node = None
|
---|
76 | self.group = None
|
---|
77 | self.omni = 0
|
---|
78 | def setNode(self, node):
|
---|
79 | self.node = node
|
---|
80 | def setGroup(self, group):
|
---|
81 | self.group = group
|
---|
82 |
|
---|
83 | class WIGroup:
|
---|
84 | def __init__(self, name):
|
---|
85 | self.name = name
|
---|
86 | self.wis = []
|
---|
87 | self.channel = None
|
---|
88 | def add_wi(self, wi):
|
---|
89 | self.wis.append(wi)
|
---|
90 | wi.setGroup(self)
|
---|
91 | def uniq(self):
|
---|
92 | self.wis = uniq(self.wis)
|
---|
93 |
|
---|
94 | class Configuration:
|
---|
95 | def __init__(self):
|
---|
96 | self.conf = None
|
---|
97 | self.calculated_score = None
|
---|
98 | def copy(self):
|
---|
99 | c = Configuration()
|
---|
100 | c.conf = self.conf.copy()
|
---|
101 | c.calculated_score = None
|
---|
102 | return c
|
---|
103 | def randomize(self):
|
---|
104 | self.conf = {}
|
---|
105 | for essid in groups.keys():
|
---|
106 | self.conf[essid] = random.randint(1, 13)
|
---|
107 | self.calculated_score = None
|
---|
108 | def score(self):
|
---|
109 | assert len(self.conf) == len(groups)
|
---|
110 | if self.calculated_score != None:
|
---|
111 | return self.calculated_score
|
---|
112 | for essid in groups.keys():
|
---|
113 | groups[essid].channel = self.conf[essid]
|
---|
114 | score = 0
|
---|
115 | for node in nodes.values():
|
---|
116 | score = score + node.score()
|
---|
117 | self.calculated_score = score
|
---|
118 | return score
|
---|
119 | def mutate(self, rate):
|
---|
120 | for essid in groups.keys():
|
---|
121 | if random.random() <= rate:
|
---|
122 | self.conf[essid] = random.randint(1, 13)
|
---|
123 | def crossover(self, partner, numpoints):
|
---|
124 | # pick the crossover points
|
---|
125 | essids = groups.keys()
|
---|
126 | points = [random.randint(0, len(essids) - 1) for i in range(numpoints)]
|
---|
127 | points.sort()
|
---|
128 | conf = []
|
---|
129 | who = 0
|
---|
130 | lastpoint = 0
|
---|
131 | while len(points):
|
---|
132 | point = points.pop(0)
|
---|
133 | if who == 0:
|
---|
134 | l = self.conf[lastpoint:point]
|
---|
135 | else:
|
---|
136 | l = partner.conf[lastpoint:point]
|
---|
137 | who = 1 - who
|
---|
138 | conf.extend(l)
|
---|
139 | lastpoint = point
|
---|
140 | if who == 0:
|
---|
141 | l = self.conf[lastpoint:]
|
---|
142 | else:
|
---|
143 | l = partner.conf[lastpoint:]
|
---|
144 | conf.extend(l)
|
---|
145 | assert len(conf) == len(groups)
|
---|
146 | self.conf = conf
|
---|
147 |
|
---|
148 |
|
---|
149 | # BEGIN UGLY PARSING CODE
|
---|
150 |
|
---|
151 | to_resolve = [] # list of (essid, WI) tuples
|
---|
152 |
|
---|
153 | def parse_wleiden_conf(lines):
|
---|
154 | essid = None
|
---|
155 | wi = None
|
---|
156 | wis = []
|
---|
157 | for l in lines:
|
---|
158 | if wi != None:
|
---|
159 | match = re.match("^MODE=master.*", l)
|
---|
160 | if match != None:
|
---|
161 | master = 1
|
---|
162 | match = re.match("^ESSID=(.*)", l)
|
---|
163 | if match != None:
|
---|
164 | essid = match.group(1)
|
---|
165 | match = re.match("^EW[0-9]*", l)
|
---|
166 | if match != None:
|
---|
167 | if master == 1:
|
---|
168 | group = WIGroup(essid)
|
---|
169 | groups[essid] = group
|
---|
170 | group.add_wi(wi)
|
---|
171 | else:
|
---|
172 | # defer, may come way later
|
---|
173 | to_resolve.append( (essid, wi) )
|
---|
174 | master = 0
|
---|
175 | wi = None
|
---|
176 | essid = None
|
---|
177 | else:
|
---|
178 | match = re.match("^\$nodename='([^']*)';.*", l)
|
---|
179 | if match != None:
|
---|
180 | nodename = match.group(1).lower()
|
---|
181 | match = re.match("^\$config{'wi[0-9]*:[0-9].*", l)
|
---|
182 | if match != None:
|
---|
183 | continue
|
---|
184 | match = re.match("^\$config{'(wi[0-9]*).*", l)
|
---|
185 | if match != None:
|
---|
186 | wi = WI(match.group(1))
|
---|
187 | wis.append(wi)
|
---|
188 | master = 0
|
---|
189 | node = Node(nodename, wis)
|
---|
190 | nodes[nodename] = node
|
---|
191 |
|
---|
192 | # gegeven een file met filenames van wleiden.conf's, parseer ze allemaal en
|
---|
193 | # vul de datastructuren
|
---|
194 | def parse_metafile(filename):
|
---|
195 | for fname in open(filename).readlines():
|
---|
196 | parse_wleiden_conf(open(fname[:-1]).readlines())
|
---|
197 | for essid, wi in to_resolve:
|
---|
198 | if not groups.has_key(essid):
|
---|
199 | print "Warning: node %s, %s refers to unknown essid %s" % (wi.node.name, wi.name, essid)
|
---|
200 | continue
|
---|
201 | groups[essid].add_wi(wi)
|
---|
202 | for group in groups.values():
|
---|
203 | group.uniq()
|
---|
204 |
|
---|
205 | # END UGLY PARSING CODE
|
---|
206 |
|
---|
207 | def plot_configuration(conf, out):
|
---|
208 | out.write("digraph plot {\n")
|
---|
209 | for essid in groups:
|
---|
210 | out.write("\"%s\" [label=\"%s\\n(%d)\"]\n" % (essid, essid, groups[essid].channel))
|
---|
211 | for nodename in nodes.keys():
|
---|
212 | for wi in nodes[nodename].wis:
|
---|
213 | if wi.group == None:
|
---|
214 | continue
|
---|
215 | out.write("\"%s\" -> \"%s\"\n" % (nodename, wi.group.name))
|
---|
216 | out.write("}")
|
---|
217 |
|
---|
218 | parse_metafile('l')
|
---|
219 |
|
---|
220 | for essid in groups.keys():
|
---|
221 | print essid, map(lambda wi: wi.node.name, groups[essid].wis)
|
---|
222 |
|
---|
223 | for i in range(20):
|
---|
224 | conf = Configuration()
|
---|
225 | conf.randomize()
|
---|
226 | population.append(conf)
|
---|
227 |
|
---|
228 | for iteration in range(1000):
|
---|
229 | for i in range(0, 9):
|
---|
230 | p = population[i].copy()
|
---|
231 | #population[i].crossover(population[i + 10], random.randint(1, 4) * 2)
|
---|
232 | population[i + 10] = p
|
---|
233 | population[i + 10].mutate(0.05)
|
---|
234 | population.sort(lambda a, b: cmp(b.score(), a.score()))
|
---|
235 | print population[0].score(), map(lambda g: g.channel, groups.values())
|
---|
236 | plot_configuration(population[0].conf, open("foo.dot", 'w'))
|
---|
237 | os.system('neato -Gstart=foo -Goverlap=false/scale -Gsplines=true -Gsep=2 -Gratio=fill -Gnslimit=50.0 -Grotate=90 -Gsize="11,7.5" -Tps -o channelga.ps foo.dot')
|
---|
238 |
|
---|