[1928] | 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 |
|
---|
| 23 | # deze twee zijn nodig voor de evaluatie van een configuratie
|
---|
| 24 | nodes = {} # lijst van Node instanties
|
---|
| 25 | groups = [] # lijst van Group instanties
|
---|
| 26 |
|
---|
| 27 | population = []
|
---|
| 28 |
|
---|
| 29 | # gegeven een lijst, geef de lijst zonder dubbele elementen
|
---|
| 30 | def uniq(l):
|
---|
| 31 | d = {}
|
---|
| 32 | for e in l:
|
---|
| 33 | d[e] = 1
|
---|
| 34 | return d.keys()
|
---|
| 35 |
|
---|
| 36 | class Node:
|
---|
| 37 | def __init__(self, name, wis):
|
---|
| 38 | self.name = name
|
---|
| 39 | self.wis = wis
|
---|
| 40 | for wi in wis:
|
---|
| 41 | wi.setNode(self)
|
---|
| 42 | def wi_byname(self, name):
|
---|
| 43 | wi = filter(lambda wi: wi.name == name, self.wis)
|
---|
| 44 | assert len(wi) == 1
|
---|
| 45 | return wi[0]
|
---|
| 46 | def score(self):
|
---|
| 47 | score = 0
|
---|
| 48 | for i in range(len(self.wis)):
|
---|
| 49 | wi_i = self.wis[i]
|
---|
| 50 | if wi_i.group == None:
|
---|
| 51 | # orphaned interface
|
---|
| 52 | continue
|
---|
| 53 | if wi_i.group.channel == None:
|
---|
| 54 | print "huh?"
|
---|
| 55 | for j in range(len(self.wis)):
|
---|
| 56 | if i != j:
|
---|
| 57 | wi_j = self.wis[j]
|
---|
| 58 | if wi_j.group == None:
|
---|
| 59 | continue
|
---|
| 60 | diff = abs(wi_i.group.channel - wi_j.group.channel)
|
---|
| 61 | if diff > 2:
|
---|
| 62 | score = score + 1
|
---|
| 63 | else:
|
---|
| 64 | score = score - 1
|
---|
| 65 | score = score * len(self.wis)
|
---|
| 66 | return score
|
---|
| 67 |
|
---|
| 68 | class WI:
|
---|
| 69 | def __init__(self, name):
|
---|
| 70 | self.name = name
|
---|
| 71 | self.node = None
|
---|
| 72 | self.group = None
|
---|
| 73 | self.omni = 0
|
---|
| 74 | def setNode(self, node):
|
---|
| 75 | self.node = node
|
---|
| 76 | def setGroup(self, group):
|
---|
| 77 | self.group = group
|
---|
| 78 |
|
---|
| 79 | class WIGroup:
|
---|
| 80 | def __init__(self, name):
|
---|
| 81 | self.name = name
|
---|
| 82 | self.wis = []
|
---|
| 83 | self.channel = None
|
---|
| 84 | def add_wi(self, wi):
|
---|
| 85 | self.wis.append(wi)
|
---|
| 86 | wi.setGroup(self)
|
---|
| 87 |
|
---|
| 88 | class Configuration:
|
---|
| 89 | def __init__(self):
|
---|
| 90 | self.conf = None
|
---|
| 91 | self.calculated_score = None
|
---|
| 92 | def copy(self):
|
---|
| 93 | c = Configuration()
|
---|
| 94 | c.conf = self.conf[:]
|
---|
| 95 | c.calculated_score = None
|
---|
| 96 | return c
|
---|
| 97 | def randomize(self):
|
---|
| 98 | self.conf = [random.randint(1, 13) for i in range(len(groups))]
|
---|
| 99 | self.calculated_score = None
|
---|
| 100 | def score(self):
|
---|
| 101 | assert len(self.conf) == len(groups)
|
---|
| 102 | if self.calculated_score != None:
|
---|
| 103 | return self.calculated_score
|
---|
| 104 | for i in range(len(self.conf)):
|
---|
| 105 | groups[i].channel = self.conf[i]
|
---|
| 106 | score = 0
|
---|
| 107 | for node in nodes.values():
|
---|
| 108 | score = score + node.score()
|
---|
| 109 | self.calculated_score = score
|
---|
| 110 | return score
|
---|
| 111 | def mutate(self, rate):
|
---|
| 112 | for i in range(len(groups)):
|
---|
| 113 | if random.random() <= rate:
|
---|
| 114 | self.conf[i] = random.randint(1, 13)
|
---|
| 115 | def crossover(self, partner, numpoints):
|
---|
| 116 | # pick the crossover points
|
---|
| 117 | points = [random.randint(0, len(groups) - 1) for i in range(numpoints)]
|
---|
| 118 | points.sort()
|
---|
| 119 | conf = []
|
---|
| 120 | who = 0
|
---|
| 121 | lastpoint = 0
|
---|
| 122 | while len(points):
|
---|
| 123 | point = points.pop(0)
|
---|
| 124 | if who == 0:
|
---|
| 125 | l = self.conf[lastpoint:point]
|
---|
| 126 | who = 1
|
---|
| 127 | else:
|
---|
| 128 | l = partner.conf[lastpoint:point]
|
---|
| 129 | who = 0
|
---|
| 130 | conf.extend(l)
|
---|
| 131 | lastpoint = point
|
---|
| 132 | if who == 0:
|
---|
| 133 | l = self.conf[lastpoint:]
|
---|
| 134 | else:
|
---|
| 135 | l = partner.conf[lastpoint:]
|
---|
| 136 | conf.extend(l)
|
---|
| 137 | assert len(conf) == len(groups)
|
---|
| 138 | self.conf = conf
|
---|
| 139 |
|
---|
| 140 |
|
---|
| 141 | # BEGIN UGLY PARSING CODE
|
---|
| 142 |
|
---|
| 143 | ipdict = {}
|
---|
| 144 | ipnr_node = {}
|
---|
| 145 | node_ipnr = {}
|
---|
| 146 | node_links = {}
|
---|
| 147 |
|
---|
| 148 | def parse_wleiden_conf(lines):
|
---|
| 149 | ipnr = None
|
---|
| 150 | wi = None
|
---|
| 151 | peer = None
|
---|
| 152 | omni = 0
|
---|
| 153 | for l in lines:
|
---|
| 154 | if wi != None:
|
---|
| 155 | match = re.match("^\EW.*", l)
|
---|
| 156 | if match != None:
|
---|
| 157 | wi = None
|
---|
| 158 | ipnr = None
|
---|
| 159 | peer = None
|
---|
| 160 | continue
|
---|
| 161 | match = re.match("^SDESC=omni.*", l)
|
---|
| 162 | if match != None:
|
---|
| 163 | omni = 1
|
---|
| 164 | match = re.match("^IP=([0-9]*\.[0-9]*\.[0-9]*\.[0-9]*).*", l)
|
---|
| 165 | if match != None:
|
---|
| 166 | ipnr = match.group(1)
|
---|
| 167 | if not node_ipnr.has_key(nodename):
|
---|
| 168 | node_ipnr[nodename] = []
|
---|
| 169 | node_ipnr[nodename].append(ipnr)
|
---|
| 170 | ipnr_node[ipnr] = nodename
|
---|
| 171 | match = re.match("^POINT_TO_POINT=([0-9]*\.[0-9]*\.[0-9]*\.[0-9]*).*", l)
|
---|
| 172 | if match != None:
|
---|
| 173 | peer = match.group(1)
|
---|
| 174 | if not node_links.has_key(nodename):
|
---|
| 175 | node_links[nodename] = []
|
---|
| 176 | node_links[nodename].append( (peer, wi) )
|
---|
| 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]*).*", l)
|
---|
| 182 | if match != None:
|
---|
| 183 | wi = match.group(1)
|
---|
| 184 | omni = 0
|
---|
| 185 |
|
---|
| 186 | # gegeven een file met filenames van wleiden.conf's, parseer ze allemaal en
|
---|
| 187 | # vul de datastructuren
|
---|
| 188 | def parse_metafile(filename):
|
---|
| 189 | for fname in open(filename).readlines():
|
---|
| 190 | parse_wleiden_conf(open(fname[:-1]).readlines())
|
---|
| 191 | for nodename in node_ipnr.keys():
|
---|
| 192 | wis = []
|
---|
| 193 | if node_links.has_key(nodename):
|
---|
| 194 | d = {}
|
---|
| 195 | for peer, wi in node_links[nodename]:
|
---|
| 196 | if not d.has_key(wi):
|
---|
| 197 | wis.append(WI(wi))
|
---|
| 198 | d[wi] = 1
|
---|
| 199 | node = Node(nodename, wis)
|
---|
| 200 | nodes[nodename] = node
|
---|
| 201 |
|
---|
| 202 | for nodename in node_links.keys():
|
---|
| 203 | node = nodes[nodename]
|
---|
| 204 |
|
---|
| 205 | for peerip, winame in node_links[nodename]:
|
---|
| 206 | wi = node.wi_byname(winame)
|
---|
| 207 | if not ipnr_node.has_key(peerip):
|
---|
| 208 | print "Warning: node %s links to phantom IP %s" % (nodename, peerip)
|
---|
| 209 | continue
|
---|
| 210 | peername = ipnr_node[peerip]
|
---|
| 211 | bail = 0
|
---|
| 212 | for peerspeerip, peerswiname in node_links[peername]:
|
---|
| 213 | if not ipnr_node.has_key(peerspeerip):
|
---|
| 214 | print "Warning: node %s links to phantom IP %s" % (nodename, peerspeerip)
|
---|
| 215 | bail = 1
|
---|
| 216 | break
|
---|
| 217 | if ipnr_node[peerspeerip] == nodename:
|
---|
| 218 | break
|
---|
| 219 | if bail:
|
---|
| 220 | continue
|
---|
| 221 | peer = nodes[peername]
|
---|
| 222 | peerwi = peer.wi_byname(peerswiname)
|
---|
| 223 | # komt wi voor deze node al voor in de bestaande
|
---|
| 224 | # groepen?
|
---|
| 225 | group = None
|
---|
| 226 | for i in range(len(groups)):
|
---|
| 227 | if filter(lambda wi: wi.node.name == nodename and wi.name == winame, groups[i].wis) != []:
|
---|
| 228 | group = groups[i]
|
---|
| 229 | break
|
---|
| 230 | if group == None:
|
---|
| 231 | for i in range(len(groups)):
|
---|
| 232 | if filter(lambda wi: wi.node.name == peername and wi.name == peerswiname, groups[i].wis) != []:
|
---|
| 233 | group = groups[i]
|
---|
| 234 | break
|
---|
| 235 | if group == None:
|
---|
| 236 | # nee. maak een groep en voeg beide
|
---|
| 237 | # endpoints toe
|
---|
| 238 | group = WIGroup('foo')
|
---|
| 239 | group.add_wi(wi)
|
---|
| 240 | groups.append(group)
|
---|
| 241 | group.add_wi(peerwi)
|
---|
| 242 | for group in groups:
|
---|
| 243 | group.wis = uniq(group.wis)
|
---|
| 244 |
|
---|
| 245 | # END UGLY PARSING CODE
|
---|
| 246 |
|
---|
| 247 | parse_metafile('l')
|
---|
| 248 |
|
---|
| 249 | for i in range(20):
|
---|
| 250 | conf = Configuration()
|
---|
| 251 | conf.randomize()
|
---|
| 252 | population.append(conf)
|
---|
| 253 |
|
---|
| 254 | while 1:
|
---|
| 255 | for i in range(10):
|
---|
| 256 | p = population[i].copy()
|
---|
| 257 | population[i].crossover(population[i + 10], 8)
|
---|
| 258 | population[i + 10] = p
|
---|
| 259 | population[i + 10].mutate(0.01)
|
---|
| 260 | population.sort(lambda a, b: cmp(b.score(), a.score()))
|
---|
| 261 | print population[0].score(), population[0].conf
|
---|