Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added antnet simulation #27

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions showml/antnet/ant.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import random
from graph import Graph

class Ant():
graph = None

def __init__(self, source, destination):
self.source = source
self.destination = destination
self.current_node = self.source
self.path_taken = []

# def update_graph(self, graph):
# self.graph = graph

def update_destination(self, destination):
self.destination = destination

def update_source(self, source):
self.source = source

def update_current(self, curr):
self.current_node = curr

def reset_paths(self):
self.path_taken = []
self.path_to_take = []

def take_step(self):
# Return true if already reached destination
if self.current_node == self.destination:
return True

Ant.graph.get_node(self.current_node)['visited'] = True

# Take a random neighbor from current position
all_neighbors = Ant.graph.get_neighbors(self.current_node)
phero_values = Ant.graph.get_pheromones(self.current_node)
travel_length = Ant.graph.get_travel_times(self.current_node)
alpha = Ant.graph.get_alpha()
beta = Ant.graph.get_beta()

# If there are no neighbors. Special case - isolated intersection
if len(all_neighbors) == 0:
return False

# Already visited or not
chosen_neighbors = []
for neigh in all_neighbors:
if not Ant.graph.get_node(neigh)['visited']:
chosen_neighbors.append(neigh)

# Randomly select a neighbor from current node and add that in the
# path_taken list
total = 0.0
probabilities = {}
for i in range(len(all_neighbors)):
total += (phero_values[i]**alpha)*((1/travel_length[i])**beta)

# print(chosen_neighbors)
for i in range(len(chosen_neighbors)):
probabilities[chosen_neighbors[i]] = ((phero_values[i]**alpha)*((1/travel_length[i])**beta))/total

sorted_probabilities = {k: v for k, v in sorted(probabilities.items(), key=lambda item: -item[1])}
sorted_neighbors = list(sorted_probabilities)
sorted_values = list(sorted_probabilities.values())

# Selection using a threshold value epsilon
eps = 0.1
candidates = []

if len(sorted_probabilities) == 1:
candidates.append(sorted_neighbors[0])
else:
for i in range(len(sorted_probabilities)-1):
if (sorted_values[i] - sorted_values[i+1]) < eps:
candidates.append(sorted_neighbors[i])
if i == len(sorted_probabilities) - 2:
candidates.append(sorted_neighbors[i+1])
else:
candidates.append(sorted_neighbors[i])
break

# Selection using random weighted choice with the probabilities
selected_neighbor = random.choices(sorted_neighbors, weights = sorted_values, k=1)[0]

Ant.graph.update_pheromones(self.current_node , selected_neighbor)

self.path_taken.append({
'start': self.current_node,
'end': selected_neighbor,
'time_spent': Ant.graph.get_edge_time(self.current_node, selected_neighbor)
})

# Update current position
self.current_node = selected_neighbor

return False

def get_path_taken(self):
return self.path_taken

def get_graph(self):
return Ant.graph

def get_time_spent(self):
time_spent = 0
for path in self.path_taken:
time_spent += path['time_spent']

return time_spent
170 changes: 170 additions & 0 deletions showml/antnet/antnet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
from ant import Ant
import random

MIN_VAL = -99999

class AntNet(Ant):
graph = None
def __init__(self, source, destination, alpha):
super().__init__(source, destination)
self.alpha = alpha

def get_L(self, node, neighbor):
cost = node['neighbors'][neighbor]
sum_cost = 0
for n in node['neighbors']:
sum_cost += node['neighbors'][n]

return 1 - (cost/sum_cost)

def get_neighbor_prob(self, node):
node = AntNet.graph.get_node(node)

chosen_neighbors = []
all_neighbors = []
for n in node['neighbors']:
all_neighbors.append(n)
if not AntNet.graph.get_node(n)['visited']:
chosen_neighbors.append(n)

probs = []
if len(chosen_neighbors) > 0:
for n in chosen_neighbors:
L = self.get_L(node, n)
prob = (node['routing_table'][n] + self.alpha * self.get_L(node, n)) / (1 + self.alpha * (len(node['neighbors']) - 1))
probs.append((n, prob))
return probs

next_neighbor = random.choice(all_neighbors)
return [(next_neighbor, 1)]


def remove_cycle_if_any(self):
if len(self.path_taken) <= 1:
return

cycle_detected = False
start_idx = 0
end_idx = 1
for i in range(len(self.path_taken)):
start_idx = i
for j in range(i+1, len(self.path_taken)):
end_idx = j
start_path = self.path_taken[i]
end_path = self.path_taken[j]

if end_path['end'] == start_path['start']:
cycle_detected = True
break
if cycle_detected:
break

if cycle_detected:
# print("CYCLE -> ", self.path_taken)
del self.path_taken[start_idx+1:]
# print("CYCLE REMOVED -> ", self.path_taken)
self.current_node = self.path_taken[start_idx]['end']

def take_forward_step(self):
self.remove_cycle_if_any()
node = self.current_node

AntNet.graph.get_node(node)['visited'] = True
if node == self.destination:
return node

probs = self.get_neighbor_prob(node)

next_neighbor = None
max_prob = MIN_VAL
for prob in probs:
if not AntNet.graph.get_node(prob[0])['visited']:
next_neighbor = prob[0]
break
if prob[1] > max_prob:
max_prob = prob[1]
next_neighbor = prob[0]

if next_neighbor is not None:
self.path_taken.append({
'start': self.current_node,
'end': next_neighbor,
'time_taken': AntNet.graph.get_edge_time(self.current_node, next_neighbor)
})

self.current_node = next_neighbor
return self.current_node

def take_lazy_step(self):
if self.current_node == self.destination:
return self.current_node

routing_table = AntNet.graph.get_node(self.current_node)['routing_table']
# print(routing_table)
# print(max(routing_table, key=routing_table.get))
self.current_node = max(routing_table, key=routing_table.get)
return self.current_node


# Reverse the path taken
def reverse_path(self):
self.path_to_take = self.path_taken.copy()
self.path_to_take.reverse()
for path in self.path_to_take:
path['start'], path['end'] = path['end'], path['start']

def go_backward(self):
total_time_taken = 0
# if len(self.path_to_take) == 0:
# print(self.source, self.destination)
# print("path to take", self.path_to_take)
for node in self.path_to_take:
total_time_taken += node['time_taken']
# for backward ant, the actual destination is now the source since we swapped it earlier
AntNet.graph.update_traffic_stat(node['end'], self.source, node['start'], total_time_taken)

def runAntNet(G, true_source, destination, c1, c2, lm):
G.set_antnet_hyperparams(c1, c2, lm)
iterations = 500
sources = G.get_all_nodes()
sources = [s for s in sources if s != destination]
ants = []
for s in sources:
ants.append(AntNet(s, destination, 0.2))

for _ in range(1000):
AntNet.graph = G
for ant in ants:
ant.reset_paths()
ant.update_destination(destination)
ant.update_source(random.choice(sources))
ant.update_current(ant.source)

for ant in ants:
for i in range(iterations):
node = ant.take_forward_step()
if node == ant.destination:
break

# Skip ants that did not reach destination
if node != ant.destination:
print("Ant did not reach destination!\n")
continue

# Backward ant
ant.reverse_path()
ant.update_destination(ant.source)
ant.update_source(destination)

ant.go_backward()

lazy_ant = AntNet(true_source, destination, 0)
path_taken = []
for i in range(iterations):
path_taken.append(lazy_ant.current_node)
node = lazy_ant.take_lazy_step()
if node == destination:
path_taken.append(node)
break

return path_taken
Loading