Browse Source

adding basic algo implementations for a2 TSP. have not finalized annealing schedules, but otherwise fine i think. need to write 'driver' program to generate graphs and stuff

Tareef 6 years ago
parent
commit
a1b12d44e2
1 changed files with 246 additions and 0 deletions
  1. 246 0
      a2/tsp_local.py

+ 246 - 0
a2/tsp_local.py

@@ -0,0 +1,246 @@
+import sys
+import copy
+import math
+from heapq import heappush, heappop
+from secrets import randbelow
+from random import shuffle, sample
+
+distances = []
+n = 0
+
+# returns a shuffled version of the cities list while maintaining the first & last elements
+def rand_order(cities):
+    # shitty variable name
+    suburbs = cities[1:-1]
+    shuffle(suburbs)
+    suburbs.insert(0, cities[0])
+    suburbs.append(cities[-1])
+    return suburbs
+
+
+# returns distance between 2 vertices
+def dist(city1, city2):
+    return math.sqrt((city1[2] - city2[2]) ** 2 + (city1[3] - city2[3]) ** 2)
+
+
+# returns total cost/distance of a tour (list of cities)
+def length(cities):
+    sum = 0
+    for i in range(len(cities) - 1):
+        sum += dist(cities[i], cities[i+1])
+
+    return sum
+
+
+# returns the best ordered child state of a state (hill climbing, basic) & its cost
+def best_child(cities, cost):
+    bestorder = cities
+    bestcost = cost
+    
+    for i in range(1, len(cities) - 2):
+        for j in range(i, len(cities) - 1):
+            temporder = copy.deepcopy(cities)
+            tempcity = temporder[i]
+            temporder[i] = temporder[j]
+            temporder[j] = tempcity
+            templen = length(temporder)
+            if (templen < cost):
+                bestorder = temporder
+                bestcost = templen
+    
+    return (bestorder, bestcost)
+
+
+# returns the best child using tabu
+# returns the best ordered child state of a state (hill climbing, basic) & its cost
+def bester_child(cities, cost, tabu):
+    bestorder = cities
+    bestcost = cost
+    
+    for i in range(1, len(cities) - 2):
+        for j in range(i, len(cities) - 1):
+            temporder = copy.deepcopy(cities)
+            tempcity = temporder[i]
+            temporder[i] = temporder[j]
+            temporder[j] = tempcity
+            
+            if (get_hashable(temporder) in tabu):
+                continue
+            
+            templen = length(temporder)
+            if (templen < cost):
+                bestorder = temporder
+                bestcost = templen
+    
+    return (bestorder, bestcost)
+
+
+# returns a random child for simulated annealing search
+# returns the ordering and the cost of it
+def random_child(cities):
+    i = randbelow(len(cities) - 3) + 1
+    j = i
+    
+    while (j == i):
+        j = randbelow(len(cities) - 3) + 1
+        
+    temporder = copy.deepcopy(cities)
+    tempcity = temporder[i]
+    temporder[i] = temporder[j]
+    temporder[j] = tempcity
+    
+    return(temporder, length(temporder))
+
+
+# returns the basic hill climbing solution for a given input
+def sir_edmund_hillary(cities):
+    curcost = length(cities)
+    curpath = copy.deepcopy(cities)
+    
+    # keep trying things til we can no longer try things
+    while(1):
+        child = best_child(curpath, curcost)
+        if (child[1] < curcost):
+            curcost = child[1]
+            curpath = child[0]
+            continue
+        break
+        
+    return(curpath, curcost)
+
+
+# retuns a tuple of the indices (ordered) in a list of cities
+def get_hashable(cities):
+    newlist = []
+    for i in range(1, len(cities) - 1):
+        newlist.append(cities[i][1])
+        
+    return tuple(newlist)
+
+
+# returns the hill climbing solution w/tabu & sideways moves for a given input
+def tenzing_norgay(cities):
+    curcost = length(cities)
+    curpath = copy.deepcopy(cities)
+    tabu = {get_hashable(cities)}
+    laterals = 0
+    
+    # keep trying things til we can no longer try things
+    while(1):
+        child = bester_child(curpath, curcost, tabu)
+        if (child[1] < curcost):
+            curcost = child[1]
+            curpath = child[0]
+            tabu.add(get_hashable(curpath))
+            continue
+        if (child[1] == curcost and laterals < n):
+            laterals += 1
+            curcost = child[1]
+            curpath = child[0]
+            tabu.add(get_hashable(curpath))
+            continue
+        break
+        
+    return(curpath, curcost)
+
+
+# returns the hill climbing solution w/ x random restarts for a given input
+def reinhold_messner(cities, x):
+    curcost = length(cities)
+    curpath = copy.deepcopy(cities)
+    bestcost = length(cities)
+    bestpath = copy.deepcopy(cities)
+    
+    # keep trying things til we can no longer try things
+    while(x):
+        child = best_child(curpath, curcost)
+        if (child[1] < curcost):
+            curcost = child[1]
+            curpath = child[0]
+            continue
+        else:
+            if (curcost < bestcost):
+                bestcost = curcost
+                bestpath = copy.deepcopy(curpath)
+            curpath = rand_order(cities)
+            curcost = length(curpath)
+            x -= 1
+        
+    return(bestpath, bestcost)
+
+
+# returns true or false randomly, based on the distribution e^(delta / temp)
+def bad_weather(delta, temp):
+    return secrets.SystemRandom().random() < math.exp(delta / temp)
+
+
+# decrements temp by the option specified by opt: 1 is linear, 2 is logarithmic, 3 is exponential
+def colder(temp, opt):
+    if (opt == 1):
+        return temp - 25
+    if (opt == 2):
+        return temp - (math.log(temp) + 2)
+    else:
+        return temp - math.exp(-0.001 * temp)
+
+
+# returns the hill climbing solution using simulated annealing w/ schedule opt
+def beck_weathers(cities, opt):
+    temp = 1000
+    curcost = length(cities)
+    curpath = copy.deepcopy(cities)
+    
+    # keep trying things til we can no longer try things
+    while(temp > 0):
+        child = random_child(curpath)
+        delta = curcost - child[1]
+        if (delta > 0 or bad_weather(delta, temp)):
+            curcost = child[1]
+            curpath = child[0]
+        temp = colder(temp, opt)
+        
+    return(curpath, curcost)
+
+
+# solves the inputted TSP problem
+def solver(problem):
+    # import map
+    prob = problem
+    with open(prob) as file:
+        prob = file.read().splitlines()
+
+    global n
+    n = int(prob[0])
+    cities = prob[1:]
+    counter = 0
+    # convert to list of list of ints
+    # original format is: letter, coord, coord
+    # output format is: iD#, letter, coord, coord
+    for l in cities:
+        temp = l.split()
+        temp[1] = int(temp[1])
+        temp[2] = int(temp[2])
+        temp.insert(0, counter)
+        counter += 1
+        cities[cities.index(l)] = temp
+    
+    # add in goal state
+    cities.append(cities[0])
+
+    #print(solve(frontier, cities))
+    print(cities)
+    #print(randbelow(n - 1) + 1)
+    print("#####")
+    input = rand_order(cities)
+    print(input)
+    print("#####")
+    #result = reinhold_messner(input, 20)
+    result = beck_weathers(input, 1)
+    print(result[0])
+    print(result[1])
+    
+    
+def main():
+    solver("instance_10.txt")
+
+main()