I got a kind of TSP-problem and an ant colony optimization solution for it. The thing is, I don't completely understand the mechanics of updating the pheromone at the end, could someone please explain it and tell if it's correct or not?
The problem is when I have a graph with more nodes this algorithm seems to give very bad results compared to a simple greedy algorithm. So, what's the problem?
# Graph data
NODES_AMOUNT = 5
MAX_AMOUNT_NODES = 6
MONEY_LIMIT = 600
COST_RND_MIN = 50
COST_RND_MAX = 300
# ant
NUM_ANTS = 5
NUM_ITERATIONS = 10
ALPHA = 1
BETA = 0.3
RHO = 0.4
Q = 100
def ant_colony_solution(K, C, kwargs):
"""
distances: a matrix of pairwise distances between cities
num_ants: number of ants in the colony
num_iterations: number of iterations to run the algorithm
alpha: controls the relative importance of the pheromone trail
beta: controls the relative importance of the edge cost
rho: controls the evaporation of the pheromone trail
q: controls the amount of pheromone deposited by each ant
"""
money_limit = kwargs["money_limit"]
ratio = kwargs["ratio"]
num_ants = kwargs["num_ants"]
num_iterations = kwargs["num_iterations"]
alpha = kwargs["alpha"]
beta = kwargs["beta"]
rho = kwargs["rho"]
q = kwargs["q"]
def cities_to_avoid_foo(K, current_city, starting, cities_, visited, spent):
avoid = []
for city in cities_:
if city == current_city:
avoid.append(city)
continue
elif city in visited:
avoid.append(city)
continue
current_to_city = K[current_city][city]
price_to_starting = K[city][starting]
if money_limit <= spent + price_to_starting + current_to_city:
avoid.append(city)
return avoid
num_cities = K.shape[0]
pheromone = np.ones((num_cities, num_cities)) / num_cities # matrix
best_route = None
best_interest = -float('inf') #
for i in range(num_iterations):
routes = [] # will contain the routes constructed by the ants
distances_ = [] # will contain the corresponding distances.
# For each iteration, we construct a solution for each ant in the colony. We iterate num_ants times.
for _ in range(num_ants):
route = []
money_spent = 0
visited = set()
# current_city = random.randint(0, num_cities-1)
current_city = 0
starting_city = current_city
visited.add(current_city)
route.append(current_city)
while True:
cities_to_avoid = set(cities_to_avoid_foo(K, current_city, starting_city, range(num_cities), visited, money_spent))
unvisited_cities = [l for l in range(num_cities) if l not in visited and l not in cities_to_avoid]
if len(unvisited_cities) == 0:
break
prob = np.zeros(num_cities)
denom = 0
for l in range(num_cities):
if l not in cities_to_avoid:
prob[l] = pheromone[current_city][l] ** alpha * (1/K[current_city][l]) ** beta
denom += prob[l]
prob /= denom
next_city = np.random.choice(range(num_cities), p=prob)
money_spent += K[current_city][next_city]
visited.add(next_city)
route.append(next_city)
current_city = next_city
# We append the ant's route and corresponding distance to the routes and distances_ lists.
routes.append(route)
interest = get_route_interest(C, route)
distances_.append(get_route_ratio(ratio, route))
# If the ant's route is better than the best route found so far, we update the best_interest and best_route variables.
# print(route, money_spent, get_route_interest(C, route))
if interest > best_interest:
best_interest = interest
best_route = route
# print(route, money_spent, get_route_interest(C, route))
# update the pheromone levels
# After all ants have constructed a solution, we evaporate the pheromone trail by multiplying all values by rho.
# We update the pheromone trail by depositing pheromone along the routes constructed by the ants.
# The amount of pheromone deposited is proportional to the inverse of the route length (1/distance_), multiplied by a constant q.
pheromone *= (1 - rho)
for route, distance in zip(routes, distances_):
for i in route:
try: # TODO ???
pheromone[i][i+1] += q / distance
except:
pheromone[i][i] += q / distance
# pheromone[route[-1]][route[0]] += q / distance
best_route.append(best_route[0])
return {
"optimal_path": best_route,
"total_interest": get_route_interest(C, best_route),
"spent": get_route_cost(K, best_route)
}
This code block: The # TODO ??? line is not something I'm sure about Does it need a fix or something?
for route, distance in zip(routes, distances_):
for i in route:
try: # TODO ???
pheromone[i][i+1] += q / distance
except:
pheromone[i][i] += q / distance
# pheromone[route[-1]][route[0]] += q / distance`
I tried looking about it online but didn't find the exact meanings of these and the answer to the code block question.