[1937] | 1 | # experiment met evolutionary programming voor de kanalenplanning
|
---|
[1928] | 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.
|
---|
[1936] | 5 | #
|
---|
[1928] | 6 | # de score functie is nu redelijk simplistisch:
|
---|
| 7 | # - de score van een combinatie van twee interfaces op een node is 1 als er
|
---|
[1936] | 8 | # twee of meer kanalen tussen zitten, en -1 als er 1 kanaal tussen zit, -5
|
---|
| 9 | # als er geen kanaal tussen zit en -10 als de kanalen hetzelfde zijn
|
---|
[1928] | 10 | # - de score van een node is de som van de scores van alle combinaties van
|
---|
| 11 | # interfaces van die node, maal het aantal interfaces. dat laatste omdat
|
---|
| 12 | # een positieve score hebben knapper is met meer interfaces dan met minder,
|
---|
| 13 | # dus dat moet beloond. een negatieve score is erger met meer interfaces
|
---|
| 14 | # (belangrijker node, waarschijnlijk) dan met minder, dus dat moet
|
---|
| 15 | # bestraft.
|
---|
| 16 | # - de totale score is de som van de scores van de nodes
|
---|
| 17 | #
|
---|
| 18 | # het stukje "genetisch" aan het einde is afgeraffeld. mutation en
|
---|
| 19 | # recombination gebeuren op de oplossing zelf ipv op een bitstring
|
---|
| 20 | # representatie. selection is dom en neemt gewoon de helft beste oplossingen.
|
---|
[1936] | 21 | #
|
---|
| 22 | # lvoge@cs.vu.nl
|
---|
[1928] | 23 | import re
|
---|
| 24 | import random
|
---|
[1929] | 25 | import os
|
---|
[1928] | 26 |
|
---|
| 27 | # deze twee zijn nodig voor de evaluatie van een configuratie
|
---|
[1935] | 28 | nodes = {} # nodename -> Node
|
---|
| 29 | groups = {} # essid -> WIGroup
|
---|
[1928] | 30 |
|
---|
| 31 | population = []
|
---|
| 32 |
|
---|
| 33 | # gegeven een lijst, geef de lijst zonder dubbele elementen
|
---|
| 34 | def uniq(l):
|
---|
| 35 | d = {}
|
---|
| 36 | for e in l:
|
---|
| 37 | d[e] = 1
|
---|
| 38 | return d.keys()
|
---|
| 39 |
|
---|
| 40 | class Node:
|
---|
| 41 | def __init__(self, name, wis):
|
---|
| 42 | self.name = name
|
---|
| 43 | self.wis = wis
|
---|
[1937] | 44 | self.special = {}
|
---|
[1928] | 45 | for wi in wis:
|
---|
| 46 | wi.setNode(self)
|
---|
| 47 | def score(self):
|
---|
| 48 | score = 0
|
---|
| 49 | for i in range(len(self.wis)):
|
---|
| 50 | wi_i = self.wis[i]
|
---|
| 51 | if wi_i.group == None:
|
---|
| 52 | # orphaned interface
|
---|
| 53 | continue
|
---|
| 54 | if wi_i.group.channel == None:
|
---|
| 55 | print "huh?"
|
---|
| 56 | for j in range(len(self.wis)):
|
---|
| 57 | if i != j:
|
---|
| 58 | wi_j = self.wis[j]
|
---|
| 59 | if wi_j.group == None:
|
---|
| 60 | continue
|
---|
| 61 | diff = abs(wi_i.group.channel - wi_j.group.channel)
|
---|
| 62 | if diff > 2:
|
---|
| 63 | score = score + 1
|
---|
[1936] | 64 | elif diff == 2:
|
---|
| 65 | score = score - 1
|
---|
| 66 | elif diff == 1:
|
---|
| 67 | score = score - 5
|
---|
[1928] | 68 | else:
|
---|
[1936] | 69 | score = score - 10
|
---|
[1937] | 70 | if self.special.has_key(wi_i.group.channel):
|
---|
| 71 | score = score + self.special[wi_i.group.channel]
|
---|
[1928] | 72 | score = score * len(self.wis)
|
---|
| 73 | return score
|
---|
[1937] | 74 | def setSpecialScoreForChannels(self, score, channels):
|
---|
| 75 | for c in channels:
|
---|
| 76 | self.special[c] = score
|
---|
[1928] | 77 |
|
---|
[1937] | 78 | # een supernode is een verzameling subnodes, bv cetim1, cetim2 en cetim3.
|
---|
[1936] | 79 | class SuperNode(Node):
|
---|
| 80 | def __init__(self, name, nodes):
|
---|
| 81 | self.name = name
|
---|
| 82 | self.nodes = nodes
|
---|
| 83 | self.wis = []
|
---|
[1937] | 84 | self.special = {}
|
---|
[1936] | 85 | for n in nodes:
|
---|
| 86 | self.wis.extend(n.wis)
|
---|
| 87 |
|
---|
[1928] | 88 | class WI:
|
---|
| 89 | def __init__(self, name):
|
---|
| 90 | self.name = name
|
---|
| 91 | self.node = None
|
---|
| 92 | self.group = None
|
---|
| 93 | self.omni = 0
|
---|
| 94 | def setNode(self, node):
|
---|
| 95 | self.node = node
|
---|
| 96 | def setGroup(self, group):
|
---|
| 97 | self.group = group
|
---|
| 98 |
|
---|
| 99 | class WIGroup:
|
---|
| 100 | def __init__(self, name):
|
---|
| 101 | self.name = name
|
---|
| 102 | self.wis = []
|
---|
| 103 | self.channel = None
|
---|
| 104 | def add_wi(self, wi):
|
---|
| 105 | self.wis.append(wi)
|
---|
| 106 | wi.setGroup(self)
|
---|
[1935] | 107 | def uniq(self):
|
---|
| 108 | self.wis = uniq(self.wis)
|
---|
[1928] | 109 |
|
---|
[1937] | 110 | # een configuratie is een bepaalde bedeling van kanalen aan groepen. aangezien
|
---|
| 111 | # groepen keyed zijn op essid is de concrete configuratie hier een dict van
|
---|
| 112 | # essids naar kanalen.
|
---|
[1928] | 113 | class Configuration:
|
---|
| 114 | def __init__(self):
|
---|
| 115 | self.conf = None
|
---|
| 116 | self.calculated_score = None
|
---|
| 117 | def copy(self):
|
---|
| 118 | c = Configuration()
|
---|
[1935] | 119 | c.conf = self.conf.copy()
|
---|
[1928] | 120 | c.calculated_score = None
|
---|
| 121 | return c
|
---|
| 122 | def randomize(self):
|
---|
[1935] | 123 | self.conf = {}
|
---|
| 124 | for essid in groups.keys():
|
---|
| 125 | self.conf[essid] = random.randint(1, 13)
|
---|
[1928] | 126 | self.calculated_score = None
|
---|
| 127 | def score(self):
|
---|
| 128 | assert len(self.conf) == len(groups)
|
---|
| 129 | if self.calculated_score != None:
|
---|
| 130 | return self.calculated_score
|
---|
[1935] | 131 | for essid in groups.keys():
|
---|
| 132 | groups[essid].channel = self.conf[essid]
|
---|
[1928] | 133 | score = 0
|
---|
| 134 | for node in nodes.values():
|
---|
| 135 | score = score + node.score()
|
---|
| 136 | self.calculated_score = score
|
---|
| 137 | return score
|
---|
| 138 | def mutate(self, rate):
|
---|
[1935] | 139 | for essid in groups.keys():
|
---|
[1928] | 140 | if random.random() <= rate:
|
---|
[1935] | 141 | self.conf[essid] = random.randint(1, 13)
|
---|
[1928] | 142 | def crossover(self, partner, numpoints):
|
---|
| 143 | # pick the crossover points
|
---|
[1935] | 144 | essids = groups.keys()
|
---|
| 145 | points = [random.randint(0, len(essids) - 1) for i in range(numpoints)]
|
---|
[1928] | 146 | points.sort()
|
---|
[1937] | 147 | conf = {}
|
---|
[1928] | 148 | who = 0
|
---|
| 149 | lastpoint = 0
|
---|
| 150 | while len(points):
|
---|
| 151 | point = points.pop(0)
|
---|
| 152 | if who == 0:
|
---|
[1937] | 153 | d = self.conf
|
---|
[1928] | 154 | else:
|
---|
[1937] | 155 | d = partner.conf
|
---|
[1935] | 156 | who = 1 - who
|
---|
[1937] | 157 | for i in range(lastpoint, point + 1):
|
---|
| 158 | conf[essids[i]] = d[essids[i]]
|
---|
[1928] | 159 | lastpoint = point
|
---|
| 160 | if who == 0:
|
---|
[1937] | 161 | d = self.conf
|
---|
[1928] | 162 | else:
|
---|
[1937] | 163 | d = partner.conf
|
---|
| 164 | for i in range(lastpoint, len(essids)):
|
---|
| 165 | conf[essids[i]] = d[essids[i]]
|
---|
[1928] | 166 | assert len(conf) == len(groups)
|
---|
| 167 | self.conf = conf
|
---|
| 168 |
|
---|
| 169 |
|
---|
| 170 | # BEGIN UGLY PARSING CODE
|
---|
| 171 |
|
---|
[1935] | 172 | to_resolve = [] # list of (essid, WI) tuples
|
---|
[1928] | 173 |
|
---|
| 174 | def parse_wleiden_conf(lines):
|
---|
[1935] | 175 | essid = None
|
---|
[1928] | 176 | wi = None
|
---|
[1935] | 177 | wis = []
|
---|
[1928] | 178 | for l in lines:
|
---|
| 179 | if wi != None:
|
---|
[1935] | 180 | match = re.match("^MODE=master.*", l)
|
---|
[1928] | 181 | if match != None:
|
---|
[1935] | 182 | master = 1
|
---|
| 183 | match = re.match("^ESSID=(.*)", l)
|
---|
[1928] | 184 | if match != None:
|
---|
[1935] | 185 | essid = match.group(1)
|
---|
| 186 | match = re.match("^EW[0-9]*", l)
|
---|
[1928] | 187 | if match != None:
|
---|
[1935] | 188 | if master == 1:
|
---|
| 189 | group = WIGroup(essid)
|
---|
| 190 | groups[essid] = group
|
---|
| 191 | group.add_wi(wi)
|
---|
| 192 | else:
|
---|
| 193 | # defer, may come way later
|
---|
| 194 | to_resolve.append( (essid, wi) )
|
---|
| 195 | master = 0
|
---|
| 196 | wi = None
|
---|
| 197 | essid = None
|
---|
[1928] | 198 | else:
|
---|
| 199 | match = re.match("^\$nodename='([^']*)';.*", l)
|
---|
| 200 | if match != None:
|
---|
| 201 | nodename = match.group(1).lower()
|
---|
[1935] | 202 | match = re.match("^\$config{'wi[0-9]*:[0-9].*", l)
|
---|
| 203 | if match != None:
|
---|
| 204 | continue
|
---|
[1928] | 205 | match = re.match("^\$config{'(wi[0-9]*).*", l)
|
---|
| 206 | if match != None:
|
---|
[1935] | 207 | wi = WI(match.group(1))
|
---|
| 208 | wis.append(wi)
|
---|
| 209 | master = 0
|
---|
| 210 | node = Node(nodename, wis)
|
---|
| 211 | nodes[nodename] = node
|
---|
[1928] | 212 |
|
---|
| 213 | # gegeven een file met filenames van wleiden.conf's, parseer ze allemaal en
|
---|
| 214 | # vul de datastructuren
|
---|
| 215 | def parse_metafile(filename):
|
---|
| 216 | for fname in open(filename).readlines():
|
---|
| 217 | parse_wleiden_conf(open(fname[:-1]).readlines())
|
---|
[1935] | 218 | for essid, wi in to_resolve:
|
---|
| 219 | if not groups.has_key(essid):
|
---|
| 220 | print "Warning: node %s, %s refers to unknown essid %s" % (wi.node.name, wi.name, essid)
|
---|
| 221 | continue
|
---|
| 222 | groups[essid].add_wi(wi)
|
---|
| 223 | for group in groups.values():
|
---|
| 224 | group.uniq()
|
---|
[1928] | 225 |
|
---|
[1936] | 226 | def parse_coupled(filename):
|
---|
| 227 | try:
|
---|
| 228 | for line in open(filename).readlines():
|
---|
| 229 | supername, rest = line[:-1].split(':')
|
---|
| 230 | subnodes = []
|
---|
| 231 | for nodename in rest.split(' '):
|
---|
| 232 | if nodename == '':
|
---|
| 233 | continue
|
---|
| 234 | node = nodes[nodename]
|
---|
| 235 | del nodes[nodename]
|
---|
| 236 | subnodes.append(node)
|
---|
| 237 | node = SuperNode(supername, subnodes)
|
---|
| 238 | nodes[supername] = node
|
---|
| 239 | except:
|
---|
| 240 | pass
|
---|
| 241 |
|
---|
[1937] | 242 | def parse_special(filename):
|
---|
| 243 | for line in open(filename).readlines():
|
---|
| 244 | fields = line[:-1].split(' ')
|
---|
| 245 | nodename = fields[0]
|
---|
| 246 | score = int(fields[1])
|
---|
| 247 | channels = map(lambda c: int(c), fields[2:])
|
---|
| 248 | nodes[nodename].setSpecialScoreForChannels(score, channels)
|
---|
| 249 |
|
---|
[1928] | 250 | # END UGLY PARSING CODE
|
---|
| 251 |
|
---|
[1929] | 252 | def plot_configuration(conf, out):
|
---|
| 253 | out.write("digraph plot {\n")
|
---|
[1935] | 254 | for essid in groups:
|
---|
[1937] | 255 | out.write("\"%s\" [shape=box label=\"%s\\n(%d)\"]\n" % (essid, essid, conf[essid]))
|
---|
[1929] | 256 | for nodename in nodes.keys():
|
---|
| 257 | for wi in nodes[nodename].wis:
|
---|
| 258 | if wi.group == None:
|
---|
| 259 | continue
|
---|
[1935] | 260 | out.write("\"%s\" -> \"%s\"\n" % (nodename, wi.group.name))
|
---|
[1929] | 261 | out.write("}")
|
---|
| 262 |
|
---|
[1928] | 263 | parse_metafile('l')
|
---|
[1936] | 264 | parse_coupled('coupled.conf')
|
---|
[1937] | 265 | parse_special('special.conf')
|
---|
[1928] | 266 |
|
---|
[1935] | 267 | for essid in groups.keys():
|
---|
| 268 | print essid, map(lambda wi: wi.node.name, groups[essid].wis)
|
---|
| 269 |
|
---|
[1928] | 270 | for i in range(20):
|
---|
| 271 | conf = Configuration()
|
---|
| 272 | conf.randomize()
|
---|
| 273 | population.append(conf)
|
---|
| 274 |
|
---|
[1936] | 275 | last_high_score = -10000000
|
---|
| 276 | iterations_since_new_high_score = 0
|
---|
| 277 | while iterations_since_new_high_score < 1000:
|
---|
[1929] | 278 | for i in range(0, 9):
|
---|
[1928] | 279 | p = population[i].copy()
|
---|
[1935] | 280 | #population[i].crossover(population[i + 10], random.randint(1, 4) * 2)
|
---|
[1928] | 281 | population[i + 10] = p
|
---|
[1929] | 282 | population[i + 10].mutate(0.05)
|
---|
[1928] | 283 | population.sort(lambda a, b: cmp(b.score(), a.score()))
|
---|
[1936] | 284 | high_score = population[0].score()
|
---|
| 285 | if high_score > last_high_score:
|
---|
| 286 | last_high_score = high_score
|
---|
| 287 | iterations_since_new_high_score = 0
|
---|
| 288 | iterations_since_new_high_score = iterations_since_new_high_score + 1
|
---|
| 289 | print high_score
|
---|
[1929] | 290 | plot_configuration(population[0].conf, open("foo.dot", 'w'))
|
---|
| 291 | 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')
|
---|
| 292 |
|
---|