1.
Import Packages
pip install pants
Requirement already satisfied: pants in
/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/
site-packages (1.0.1)
Note: you may need to restart the kernel to use updated packages.
#import packages
import pants
import math
import random
2. Input
length function to the algorithm this is able to calculate the distances from node 𝑖
The input for ACOPants is a list of coordinates (x, y) of the nodes, and providing a
to 𝑗.
Here we have a csv .file that contains information about cities all around the world from the
webpage: http://simplemaps.com/data/world-cities. We will work with the cities from India and
with the coordinates in decimal degrees (lat and lng).
pip install openpyxl
Requirement already satisfied: openpyxl in
/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/
site-packages (3.1.5)
Requirement already satisfied: et-xmlfile in
/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/
site-packages (from openpyxl) (2.0.0)
Note: you may need to restart the kernel to use updated packages.
import pandas as pd
import numpy as np
cities = pd.read_csv('worldcities.csv')
INDIAcities = cities.loc[cities['country'] == 'India'] #only the
cities that belong to INDIA
print('Dimention INDIAcities:', INDIAcities.shape) #dimention of
INDIAcities dataset
Dimention INDIAcities: (7031, 11)
3. View the data
#Get sample of 100 cities
Indiancities = INDIAcities.sample(100) #to get a sample of 100 rows to
work with
print('Dimention Indiancities:', Indiancities.shape) #dimention
UScities dataset
Indiancities.head() #fisrt rows from the new dataset
Dimention Indiancities: (100, 11)
              city   city_ascii       lat       lng country iso2 iso3   \
7343    Mannārgudi   Mannargudi   10.6653   79.4521   India   IN IND
11733      Bhatkal      Bhatkal   13.9853   74.5553   India   IN IND
21181   Mogalturru   Mogalturru   16.4167   81.6000   India   IN IND
9315        Nagari       Nagari   13.3214   79.5856   India   IN IND
12848    Bairāgnia    Bairagnia   26.7381   85.2736   India   IN IND
            admin_name capital    population           id
7343        Tamil Nādu     NaN       81150.0   1356628918
11733        Karnātaka     NaN       47748.0   1356324455
21181   Andhra Pradesh     NaN       24189.0   1356138893
9315    Andhra Pradesh     NaN       62253.0   1356993418
12848            Bihār     NaN       42895.0   1356258895
4. Define the Distance between edges
#To calculate the distances from node 𝑖 to 𝑗, we are going to use
Euclidean distance, which is the straight-line distance between two
points or nodes.
def euclidean(a, b):
    return math.sqrt(pow(a[1] - b[1], 2) + pow(a[0] - b[0], 2))
5. Node, Edge initialization
#Since the input is a list of nodes(x,y):
x = Indiancities['lat']
y = Indiancities['lng']
DD = list(zip(x,y)) #Indiancities represented in decimal degrees
print(DD)
[(10.6653, 79.4521), (13.9853, 74.5553), (16.4167, 81.6), (13.3214,
79.5856), (26.7381, 85.2736), (11.9842, 79.6321), (24.0898, 87.9902),
(28.068, 78.751), (11.042, 76.0815), (27.43, 83.42), (28.4931,
79.0853), (21.63, 69.98), (11.5833, 75.7667), (19.77, 74.48), (23.03,
70.22), (23.5582, 79.678), (15.2333, 78.3167), (27.32, 82.42),
(26.8951, 74.3222), (25.7671, 85.7245), (15.6406, 75.9531), (12.2064,
78.1906), (21.2141, 86.1249), (26.6396, 84.5108), (11.8611, 75.5625),
(14.0078, 77.6086), (8.9127, 78.0218), (15.7441, 77.476), (8.742,
76.8941), (12.8899, 78.8394), (22.2211, 85.6392), (12.9623, 80.1986),
(10.439, 77.9546), (9.65, 76.7167), (28.6794, 77.0284), (29.09,
79.19), (16.1589, 79.3986), (24.1341, 79.6012), (25.8012, 86.3229),
(22.85, 72.5833), (23.8735, 86.1516), (24.1692, 75.2342), (26.9873,
84.0752), (16.7833, 81.9), (25.4252, 83.8201), (13.5427, 80.069),
(13.6308, 74.7377), (25.2958, 84.8789), (8.6976, 76.9582), (25.5111,
84.4946), (25.3679, 87.0026), (22.7758, 88.0054), (24.1908, 82.7817),
(22.891, 88.473), (25.663, 87.5378), (12.5133, 78.1844), (24.0437,
78.3301), (13.26, 76.48), (18.9586, 78.361), (25.234, 83.9215),
(9.9649, 78.1416), (33.8789, 75.2485), (14.5237, 80.1521), (11.9167,
75.55), (21.8706, 73.5028), (28.393, 77.048), (12.1078, 78.834),
(25.3796, 85.5376), (11.3597, 76.7649), (10.995, 76.5053), (23.0674,
72.5121), (25.1859, 85.9234), (12.15, 77.1), (11.8111, 76.0556),
(25.9022, 84.5055), (11.2559, 76.8872), (12.7409, 77.8253), (23.3456,
85.1114), (25.5209, 85.4614), (24.9564, 86.0344), (29.086, 76.582),
(17.3477, 81.6041), (25.3987, 85.7856), (14.0333, 80.05), (10.3774,
79.8495), (13.9667, 79.5833), (11.4607, 76.7182), (12.3329, 76.862),
(14.4267, 78.7618), (26.2016, 88.1095), (8.2055, 77.5755), (25.9711,
85.9148), (31.28, 74.86), (9.0, 76.6167), (31.55, 75.63), (26.5714,
86.0903), (26.1583, 84.757), (23.9955, 77.1395), (26.0831, 84.8447),
(20.2871, 83.1466)]
Hyper Parameters
Optional arguments: -a A, --alpha A relative importance placed on pheromones; default=1 -b B,
--beta B relative importance placed on distances; default=3 -l L, --limit L number of iterations to
perform; default=100 -p P, --rho P ratio of evaporated pheromone (0 <= P <= 1); default=0.8 -e
E, --elite E ratio of elite ant's pheromone; default=0.5 -q Q, --Q Q total pheromone capacity of
each ant (Q > 0); default=1 -t T, --t0 T initial amount of pheromone on every edge (T > 0);
default=0.01 -c N, --count N number of ants used in each iteration (N > 0); default=10
Arguments are very important and they can affect the result. Usually, it is used as many number
of ants (N) as nodes. Also, is better to use a higher value of beta(distance) than
beta(pheromone).
6 Create the World
#Here we will use a number of ants less than number of nodes (N= 5).
#Number of iterations L = 5 instead of 100.
#Alpha and beta with the same relative importance (A, B = 1)
world = pants.World(DD, euclidean, N = 5, L = 5 , A = 1, B = 1)
----------------------------------------------------------------------
-----
AttributeError                            Traceback (most recent call
last)
Cell In[9], line 5
      1 #Here we will use a number of ants less than number of nodes
(N= 5).
      2 #Number of iterations L = 5 instead of 100.
      3 #Alpha and beta with the same relative importance (A, B = 1)
----> 5 world = pants.World(DD, euclidean, N = 5, L = 5 , A = 1, B =
1)
AttributeError: module 'pants' has no attribute 'World'
7 The solver and the solution
solver = pants.Solver()
solution = solver.solve(world)
print('DISTANCE:', solution.distance) #total distance of the tour
performed
tour = solution.tour    #nodes visited in order
print(tour)
DISTANCE: 129.0580588554432
[(26.6128, 89.825), (25.8738, 87.9637), (25.8564, 87.9124), (25.8723,
87.8978), (22.4156, 88.3051), (22.2194, 88.2142), (22.22, 88.17),
(22.6236, 87.9195), (22.2847, 88.4053), (22.1745, 88.4184), (22.3465,
88.9167), (22.5475, 88.6606), (22.65, 88.29), (22.737, 88.1918),
(22.81, 88.23), (22.79, 88.32), (23.5984, 88.0871), (23.25, 87.85),
(23.2091, 87.6983), (23.3427, 87.6885), (23.8, 87.38), (23.8229,
86.9839), (22.8, 86.95), (23.5142, 86.498), (23.37, 85.97), (23.37,
85.9), (24.4663, 87.9022), (24.3841, 87.884), (24.4724, 87.9589),
(24.1472, 87.8802), (24.2463, 87.8509), (24.0912, 88.4947), (20.17,
85.7), (19.32, 84.8), (17.0185, 82.2349), (17.08, 82.13), (17.2333,
82.2), (17.605, 82.408), (17.67, 82.62), (16.93, 81.63), (16.95,
80.7833), (17.0331, 80.8056), (16.8991, 81.1764), (16.801, 80.63),
(16.5167, 80.6167), (16.3785, 80.6146), (16.3036, 80.6172), (16.517,
81.988), (16.4814, 81.6267), (16.5333, 81.5333), (16.6, 81.4667),
(16.0667, 80.5667), (15.9806, 80.6347), (15.9333, 80.55), (15.994,
80.378), (15.298, 80.034), (14.5942, 80.0297), (14.8667, 79.3167),
(13.3667, 79.1833), (13.514, 78.227), (13.85, 78.2667), (13.8408,
78.3056), (13.7611, 78.425), (14.1667, 78.7), (14.4167, 78.2333),
(13.8032, 77.6097), (13.9369, 77.2694), (13.3667, 78.4333), (12.4972,
76.8878), (12.4181, 76.6947), (11.65, 78.1667), (10.8, 79.15),
(8.7833, 78.1333), (16.5787, 79.8756), (16.2372, 79.8464), (17.1528,
79.6861), (17.2, 80.55), (16.892, 80.287), (16.6167, 77.85), (17.1447,
78.2886), (17.8517, 78.6828), (18.3597, 79.0875), (18.4667, 78.8833),
(18.83, 79.45), (17.9756, 79.6011), (16.2833, 78.5167), (16.2319,
76.9553), (16.0528, 76.8877), (14.6, 74.8333), (15.48, 73.83),
(15.6294, 73.7358), (17.72, 73.38), (20.9, 74.7833), (21.45, 80.2),
(18.7667, 84.1667), (28.45, 77.02), (29.5833, 74.3167), (30.6167,
74.8), (30.9083, 75.8486), (34.4225, 74.6375)]
#To get the names of the cities visited from the nodes values:
Indiancities.set_index(['lat','lng'])['city'].loc[tour].tolist()
['Kumārgrām',
 'Gopālpur',
 'Rānīganj',
 'Bagela',
 'Amgachia',
 'Pātra',
 'Amtala',
 'Jhikra',
 'Multi',
 'Jaynagar-Majilpur',
 'Khulna',
 'Kamargani',
 'Jagdispur',
 'Mashāt',
 'Singur',
 'Baidyabāti',
 'Srikhanda',
 'Barddhamān',
 'Khandaghosh',
 'Galsi',
 'Dubrājpur',
 'Pānuria',
 'Raipur',
 'Pāra',
 'Jhalidā',
 'Tūlin',
 'Kāthia',
 'Rudra Nagar',
 'Jājigrām',
 'Baswa',
 'Mehegrām',
 'Bhagirathpur',
 'Jatani',
 'Brahmapur',
 'Panasapādu',
 'Peddāpuram',
 'Prattipādu',
 'Nātavaram',
 'Narsīpatnam',
 'Kalavalapalle',
 'Vissannapeta',
 'Channubanda',
'Tadikalapūdi',
'Chandragūdem',
'Bezwāda',
'Chiluvūru',
'Kolakalūru',
'Bandamūrlanka',
'Matsyapuri',
'Bhīmavaram',
'Undi',
'Ponnūru',
'Pittalavānipālem',
'Karlapālem',
'Cherukūru',
'Mūlaguntapādu',
'Vidavalūru',
'Udayagiri',
'Penumūr',
'Gavunipalli',
'Somapalle',
'Mulakalacheruvu',
'Kosuvāripalle',
'Lakkireddipalle',
'Pulivendla',
'Lepākshi',
'Madakasīra',
'Rāmasamudram',
'Sante Kasalgere',
'Shrīrangapattana',
'Salem',
'Tanjore',
'Tuticorin',
'Kottapālem',
'Muppālla',
'Chiwemla',
'Kallūr',
'Anigandlapādu',
'Devarkadra',
'Kotūr',
'Gajwel',
'Timmāpuram',
'Vemalwāda',
'Nāspur',
'Warangal',
'Lingāl',
'Kyādgeri',
'Bāgalvād',
'Kālkuni',
'Panaji',
'Morgim',
'Khed',
'Dhūlia',
'Gondiā',
'Meliyāputtu',
'Gurgaon',
'Hanumāngarh',
'Sandhwān',
'Ludhiāna',
'Bandipura']
Run with different set of parameters
#Here we will use a number of ants bigger than the number of nodes (N=
100).
#Number of iterations L = 150.
#Alpha and beta with the different relative importance, distance
(beta) will be more importat. (A = 2, B = 3)
world = pants.World(DD, euclidean, N = 150, L = 150 , A = 2, B = 3)
solver = pants.Solver()
solution = solver.solve(world)
print('DISTANCE:', solution.distance) #total distance of the tour
performed
tour1 = solution.tour    #nodes visited in order
print(tour1)
DISTANCE: 136.13490737264505
[(23.5142, 86.498), (23.8229, 86.9839), (23.37, 85.9), (23.37, 85.97),
(23.5984, 88.0871), (23.8, 87.38), (22.8, 86.95), (22.6236, 87.9195),
(22.4156, 88.3051), (22.65, 88.29), (22.2847, 88.4053), (22.1745,
88.4184), (22.2194, 88.2142), (22.22, 88.17), (24.0912, 88.4947),
(24.4724, 87.9589), (24.4663, 87.9022), (24.2463, 87.8509), (24.3841,
87.884), (24.1472, 87.8802), (23.25, 87.85), (23.2091, 87.6983),
(23.3427, 87.6885), (25.8738, 87.9637), (25.8723, 87.8978), (25.8564,
87.9124), (22.79, 88.32), (22.81, 88.23), (22.737, 88.1918), (22.5475,
88.6606), (22.3465, 88.9167), (19.32, 84.8), (18.7667, 84.1667),
(17.67, 82.62), (17.2333, 82.2), (17.08, 82.13), (17.0185, 82.2349),
(17.605, 82.408), (16.0667, 80.5667), (15.9333, 80.55), (16.3785,
80.6146), (16.3036, 80.6172), (16.5167, 80.6167), (16.801, 80.63),
(16.95, 80.7833), (17.0331, 80.8056), (17.2, 80.55), (16.892, 80.287),
(17.1528, 79.6861), (17.8517, 78.6828), (18.4667, 78.8833), (18.3597,
79.0875), (18.83, 79.45), (17.9756, 79.6011), (15.994, 80.378),
(15.9806, 80.6347), (16.8991, 81.1764), (16.93, 81.63), (16.4814,
81.6267), (16.5333, 81.5333), (16.6, 81.4667), (16.517, 81.988),
(16.2372, 79.8464), (16.5787, 79.8756), (14.8667, 79.3167), (13.3667,
78.4333), (13.514, 78.227), (13.85, 78.2667), (13.8408, 78.3056),
(13.7611, 78.425), (13.8032, 77.6097), (13.9369, 77.2694), (14.4167,
78.2333), (14.1667, 78.7), (13.3667, 79.1833), (14.5942, 80.0297),
(15.298, 80.034), (17.1447, 78.2886), (16.2833, 78.5167), (16.6167,
77.85), (16.0528, 76.8877), (16.2319, 76.9553), (15.48, 73.83),
(15.6294, 73.7358), (14.6, 74.8333), (12.4181, 76.6947), (12.4972,
76.8878), (11.65, 78.1667), (10.8, 79.15), (8.7833, 78.1333), (17.72,
73.38), (20.9, 74.7833), (28.45, 77.02), (29.5833, 74.3167), (30.6167,
74.8), (30.9083, 75.8486), (34.4225, 74.6375), (21.45, 80.2), (20.17,
85.7), (26.6128, 89.825)]
Indiancities.set_index(['lat','lng'])['city'].loc[tour1].tolist()
['Pāra',
 'Pānuria',
 'Tūlin',
 'Jhalidā',
 'Srikhanda',
 'Dubrājpur',
 'Raipur',
 'Jhikra',
 'Amgachia',
 'Jagdispur',
 'Multi',
 'Jaynagar-Majilpur',
 'Pātra',
 'Amtala',
 'Bhagirathpur',
 'Jājigrām',
 'Kāthia',
 'Mehegrām',
 'Rudra Nagar',
 'Baswa',
 'Barddhamān',
 'Khandaghosh',
 'Galsi',
 'Gopālpur',
 'Bagela',
 'Rānīganj',
 'Baidyabāti',
 'Singur',
 'Mashāt',
 'Kamargani',
 'Khulna',
 'Brahmapur',
 'Meliyāputtu',
 'Narsīpatnam',
 'Prattipādu',
 'Peddāpuram',
 'Panasapādu',
 'Nātavaram',
 'Ponnūru',
 'Karlapālem',
'Chiluvūru',
'Kolakalūru',
'Bezwāda',
'Chandragūdem',
'Vissannapeta',
'Channubanda',
'Kallūr',
'Anigandlapādu',
'Chiwemla',
'Gajwel',
'Vemalwāda',
'Timmāpuram',
'Nāspur',
'Warangal',
'Cherukūru',
'Pittalavānipālem',
'Tadikalapūdi',
'Kalavalapalle',
'Matsyapuri',
'Bhīmavaram',
'Undi',
'Bandamūrlanka',
'Muppālla',
'Kottapālem',
'Udayagiri',
'Rāmasamudram',
'Gavunipalli',
'Somapalle',
'Mulakalacheruvu',
'Kosuvāripalle',
'Lepākshi',
'Madakasīra',
'Pulivendla',
'Lakkireddipalle',
'Penumūr',
'Vidavalūru',
'Mūlaguntapādu',
'Kotūr',
'Lingāl',
'Devarkadra',
'Bāgalvād',
'Kyādgeri',
'Panaji',
'Morgim',
'Kālkuni',
'Shrīrangapattana',
'Sante Kasalgere',
'Salem',
'Tanjore',
'Tuticorin',
'Khed',
'Dhūlia',
'Gurgaon',
'Hanumāngarh',
'Sandhwān',
'Ludhiāna',
'Bandipura',
'Gondiā',
'Jatani',
'Kumārgrām']