# experiment met een genetisch algoritme voor de kanalenplanning # het genetische zit er nu nauwelijks in, en wat er in zit is ongetwijfeld # fout. wat er wel goed in zit is het parseren van de bestaande situatie en # het scoren van (nu random) configuraties. # de score functie is nu redelijk simplistisch: # - de score van een combinatie van twee interfaces op een node is 1 als er # twee of meer kanalen tussen zitten, en -1 als dat niet zo is # - de score van een node is de som van de scores van alle combinaties van # interfaces van die node, maal het aantal interfaces. dat laatste omdat # een positieve score hebben knapper is met meer interfaces dan met minder, # dus dat moet beloond. een negatieve score is erger met meer interfaces # (belangrijker node, waarschijnlijk) dan met minder, dus dat moet # bestraft. # - de totale score is de som van de scores van de nodes # # het stukje "genetisch" aan het einde is afgeraffeld. mutation en # recombination gebeuren op de oplossing zelf ipv op een bitstring # representatie. selection is dom en neemt gewoon de helft beste oplossingen. import re import random import os # deze twee zijn nodig voor de evaluatie van een configuratie nodes = {} # lijst van Node instanties groups = [] # lijst van Group instanties population = [] # gegeven een lijst, geef de lijst zonder dubbele elementen def uniq(l): d = {} for e in l: d[e] = 1 return d.keys() class Node: def __init__(self, name, wis): self.name = name self.wis = wis for wi in wis: wi.setNode(self) def wi_byname(self, name): wi = filter(lambda wi: wi.name == name, self.wis) assert len(wi) == 1 return wi[0] def score(self): score = 0 for i in range(len(self.wis)): wi_i = self.wis[i] if wi_i.group == None: # orphaned interface continue if wi_i.group.channel == None: print "huh?" for j in range(len(self.wis)): if i != j: wi_j = self.wis[j] if wi_j.group == None: continue diff = abs(wi_i.group.channel - wi_j.group.channel) if diff > 2: score = score + 1 else: score = score - 1 score = score * len(self.wis) return score class WI: def __init__(self, name): self.name = name self.node = None self.group = None self.omni = 0 def setNode(self, node): self.node = node def setGroup(self, group): self.group = group class WIGroup: def __init__(self, name): self.name = name self.wis = [] self.channel = None def add_wi(self, wi): self.wis.append(wi) wi.setGroup(self) class Configuration: def __init__(self): self.conf = None self.calculated_score = None def copy(self): c = Configuration() c.conf = self.conf[:] c.calculated_score = None return c def randomize(self): self.conf = [random.randint(1, 13) for i in range(len(groups))] self.calculated_score = None def score(self): assert len(self.conf) == len(groups) if self.calculated_score != None: return self.calculated_score for i in range(len(self.conf)): groups[i].channel = self.conf[i] score = 0 for node in nodes.values(): score = score + node.score() self.calculated_score = score return score def mutate(self, rate): for i in range(len(groups)): if random.random() <= rate: self.conf[i] = random.randint(1, 13) def crossover(self, partner, numpoints): # pick the crossover points points = [random.randint(0, len(groups) - 1) for i in range(numpoints)] points.sort() conf = [] who = 0 lastpoint = 0 while len(points): point = points.pop(0) if who == 0: l = self.conf[lastpoint:point] who = 1 else: l = partner.conf[lastpoint:point] who = 0 conf.extend(l) lastpoint = point if who == 0: l = self.conf[lastpoint:] else: l = partner.conf[lastpoint:] conf.extend(l) assert len(conf) == len(groups) self.conf = conf # BEGIN UGLY PARSING CODE ipdict = {} ipnr_node = {} node_ipnr = {} node_links = {} def parse_wleiden_conf(lines): ipnr = None wi = None peer = None omni = 0 for l in lines: if wi != None: match = re.match("^\EW.*", l) if match != None: wi = None ipnr = None peer = None continue match = re.match("^SDESC=omni.*", l) if match != None: omni = 1 match = re.match("^IP=([0-9]*\.[0-9]*\.[0-9]*\.[0-9]*).*", l) if match != None: ipnr = match.group(1) if not node_ipnr.has_key(nodename): node_ipnr[nodename] = [] node_ipnr[nodename].append(ipnr) ipnr_node[ipnr] = nodename match = re.match("^POINT_TO_POINT=([0-9]*\.[0-9]*\.[0-9]*\.[0-9]*).*", l) if match != None: peer = match.group(1) if not node_links.has_key(nodename): node_links[nodename] = [] node_links[nodename].append( (peer, wi) ) else: match = re.match("^\$nodename='([^']*)';.*", l) if match != None: nodename = match.group(1).lower() match = re.match("^\$config{'(wi[0-9]*).*", l) if match != None: wi = match.group(1) omni = 0 # gegeven een file met filenames van wleiden.conf's, parseer ze allemaal en # vul de datastructuren def parse_metafile(filename): for fname in open(filename).readlines(): parse_wleiden_conf(open(fname[:-1]).readlines()) for nodename in node_ipnr.keys(): wis = [] if node_links.has_key(nodename): d = {} for peer, wi in node_links[nodename]: if not d.has_key(wi): wis.append(WI(wi)) d[wi] = 1 node = Node(nodename, wis) nodes[nodename] = node for nodename in node_links.keys(): node = nodes[nodename] for peerip, winame in node_links[nodename]: wi = node.wi_byname(winame) if not ipnr_node.has_key(peerip): print "Warning: node %s links to phantom IP %s" % (nodename, peerip) continue peername = ipnr_node[peerip] bail = 0 for peerspeerip, peerswiname in node_links[peername]: if not ipnr_node.has_key(peerspeerip): print "Warning: node %s links to phantom IP %s" % (nodename, peerspeerip) bail = 1 break if ipnr_node[peerspeerip] == nodename: break if bail: continue peer = nodes[peername] peerwi = peer.wi_byname(peerswiname) # komt wi voor deze node al voor in de bestaande # groepen? group = None for i in range(len(groups)): if filter(lambda wi: wi.node.name == nodename and wi.name == winame, groups[i].wis) != []: group = groups[i] break if group == None: for i in range(len(groups)): if filter(lambda wi: wi.node.name == peername and wi.name == peerswiname, groups[i].wis) != []: group = groups[i] break if group == None: # nee. maak een groep en voeg beide # endpoints toe group = WIGroup("group%d" % len(groups)) group.add_wi(wi) groups.append(group) group.add_wi(peerwi) for group in groups: group.wis = uniq(group.wis) # END UGLY PARSING CODE def plot_configuration(conf, out): out.write("digraph plot {\n") for i in range(len(groups)): out.write("group%s [label=\"%d\"]\n" % (i, conf[i])) for nodename in nodes.keys(): for wi in nodes[nodename].wis: if wi.group == None: continue out.write("%s -> %s\n" % (nodename, wi.group.name)) out.write("}") parse_metafile('l') for i in range(20): conf = Configuration() conf.randomize() population.append(conf) for iteration in range(1000): for i in range(0, 9): p = population[i].copy() population[i].crossover(population[i + 10], random.randint(1, 4) * 2) population[i + 10] = p population[i + 10].mutate(0.05) population.sort(lambda a, b: cmp(b.score(), a.score())) print population[0].score() plot_configuration(population[0].conf, open("foo.dot", 'w')) 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')