├── EVRPTW ├── ACO │ ├── __init__.py │ ├── utils │ │ ├── __init__.py │ │ ├── plot │ │ │ ├── __init__.py │ │ │ ├── plot_penalty.py │ │ │ ├── plot_fitness.py │ │ │ ├── plot_fitness_penalty.py │ │ │ └── gif_and_path_plot.py │ │ └── percentage_feasible_solution.py │ ├── tuning │ │ ├── __init__.py │ │ ├── tuning_k1.py │ │ ├── tuning_k1_k2.py │ │ └── tuning_alpha_beta_q0.py │ ├── config │ │ ├── aco_config.yaml │ │ └── dask_config.yaml │ ├── paper_total_instances │ │ ├── c103C5.txt │ │ ├── c101C5.txt │ │ ├── c208C5.txt │ │ ├── r104C5.txt │ │ ├── r105C5.txt │ │ ├── r202C5.txt │ │ ├── rc208C5.txt │ │ ├── r203C5.txt │ │ ├── rc105C5.txt │ │ ├── c206C5.txt │ │ ├── rc108C5.txt │ │ ├── rc204C5.txt │ │ ├── c205C10.txt │ │ ├── r103C10.txt │ │ ├── r102C10.txt │ │ ├── rc205C10.txt │ │ ├── c104C10.txt │ │ ├── r201C10.txt │ │ ├── rc102C10.txt │ │ ├── rc108C10.txt │ │ ├── rc201C10.txt │ │ ├── c101C10.txt │ │ ├── c202C10.txt │ │ ├── r203C10.txt │ │ ├── c106C15.txt │ │ ├── c208C15.txt │ │ ├── c202C15.txt │ │ ├── rc108C15.txt │ │ ├── r209C15.txt │ │ ├── rc202C15.txt │ │ ├── rc103C15.txt │ │ ├── r105C15.txt │ │ ├── r202C15.txt │ │ ├── rc204C15.txt │ │ └── r102C15.txt │ ├── penality_instances │ │ ├── c103C5.txt │ │ ├── c101C5.txt │ │ ├── c104C10.txt │ │ ├── c101C10.txt │ │ └── c106C15.txt │ ├── test_instances │ │ ├── r201C10.txt │ │ ├── rc108C15.txt │ │ ├── c202C15.txt │ │ ├── r209C15.txt │ │ ├── rc202C15.txt │ │ ├── rc103C15.txt │ │ ├── r105C15.txt │ │ ├── r202C15.txt │ │ ├── rc204C15.txt │ │ └── r102C15.txt │ ├── target.py │ ├── main.py │ ├── acsd.py │ └── evrptw_config.py ├── tests │ ├── __init__.py │ ├── test_target.py │ ├── test_acsd.py │ ├── test_evrptw_config.py │ └── test_ant.py ├── active_venv.sh ├── gif │ └── path_evolution.gif ├── img │ └── fitness_result_Run_2.png ├── .gitignore ├── requirements.txt ├── github │ └── dependabot.yml ├── start_test.sh ├── docs │ ├── target.md │ ├── main.md │ ├── acsd.md │ ├── evrptw_config.md │ └── ant.md └── results │ └── comparisionResult.md ├── .gitignore └── README.md /EVRPTW/ACO/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /EVRPTW/ACO/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /EVRPTW/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /EVRPTW/ACO/tuning/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /EVRPTW/ACO/utils/plot/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /EVRPTW/active_venv.sh: -------------------------------------------------------------------------------- 1 | source "${PWD}/env/bin/activate" 2 | -------------------------------------------------------------------------------- /EVRPTW/gif/path_evolution.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F-a-b-r-i-z-i-o/Ant_Colony_Optimization_for_Evrptw/HEAD/EVRPTW/gif/path_evolution.gif -------------------------------------------------------------------------------- /EVRPTW/img/fitness_result_Run_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F-a-b-r-i-z-i-o/Ant_Colony_Optimization_for_Evrptw/HEAD/EVRPTW/img/fitness_result_Run_2.png -------------------------------------------------------------------------------- /EVRPTW/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | 3 | EVRPTW/ 4 | 5 | .pytest_cache 6 | .__pycache__ 7 | .DS_Store 8 | .idea/ 9 | *.swp 10 | 11 | lib/ 12 | bin/ 13 | pyvenv.cfg 14 | env/ -------------------------------------------------------------------------------- /EVRPTW/requirements.txt: -------------------------------------------------------------------------------- 1 | dask==2024.1.0 2 | distlib==0.3.8 3 | distributed==2023.10.0 4 | matplotlib==3.8.2 5 | numpy==1.26.2 6 | pandas==2.2.0 7 | pytest==7.4.3 8 | virtualenv==20.26.6 9 | -------------------------------------------------------------------------------- /EVRPTW/github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "pip" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /EVRPTW/ACO/config/aco_config.yaml: -------------------------------------------------------------------------------- 1 | aco_config: 2 | num_runs: 15 3 | ants_num: 10 4 | max_iter: 5000 5 | alpha: 1 6 | beta: 1 7 | q0: 0.7 8 | k1: 89 9 | k2: 0 10 | rho: 0.2 11 | apply_local_search: True 12 | save_path_details: False 13 | 14 | -------------------------------------------------------------------------------- /EVRPTW/start_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export PYTHONPATH="${PWD}" 4 | 5 | 6 | pytest "${PWD}/tests/test_target.py" -v 7 | pytest "${PWD}/tests/test_evrptw_config.py" -v 8 | pytest "${PWD}/tests/test_ant.py" -v 9 | pytest "${PWD}/tests/test_acsd.py" -v 10 | 11 | 12 | -------------------------------------------------------------------------------- /EVRPTW/ACO/config/dask_config.yaml: -------------------------------------------------------------------------------- 1 | dask_config: 2 | n_workers: 15 # Number of worker processes to spawn 3 | threads_per_worker: 1 # Number of threads each worker process should use 4 | memory_limit: '4GB' # Memory limit to assign to each worker. 'auto' lets Dask decide 5 | processes: True # Separate process with True value 6 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/c103C5.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 1236.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 1236.0 0.0 4 | S15 f 39.0 26.0 0.0 0.0 1236.0 0.0 5 | C20 c 30.0 50.0 10.0 0.0 1136.0 90.0 6 | C98 c 58.0 75.0 20.0 0.0 1115.0 90.0 7 | C65 c 48.0 40.0 10.0 67.0 139.0 90.0 8 | C57 c 40.0 15.0 40.0 989.0 1063.0 90.0 9 | C24 c 25.0 50.0 10.0 0.0 1131.0 90.0 10 | 11 | Q Vehicle fuel tank capacity /77.75/ 12 | C Vehicle load capacity /200.0/ 13 | r fuel consumption rate /1.0/ 14 | g inverse refueling rate /3.47/ 15 | v average Velocity /1.0/ 16 | -------------------------------------------------------------------------------- /EVRPTW/ACO/penality_instances/c103C5.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 1236.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 1236.0 0.0 4 | S15 f 39.0 26.0 0.0 0.0 1236.0 0.0 5 | C20 c 30.0 50.0 10.0 0.0 1136.0 90.0 6 | C98 c 58.0 75.0 20.0 0.0 1115.0 90.0 7 | C65 c 48.0 40.0 10.0 67.0 139.0 90.0 8 | C57 c 40.0 15.0 40.0 989.0 1063.0 90.0 9 | C24 c 25.0 50.0 10.0 0.0 1131.0 90.0 10 | 11 | Q Vehicle fuel tank capacity /77.75/ 12 | C Vehicle load capacity /200.0/ 13 | r fuel consumption rate /1.0/ 14 | g inverse refueling rate /3.47/ 15 | v average Velocity /1.0/ 16 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/c101C5.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 1236.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 1236.0 0.0 4 | S5 f 31.0 84.0 0.0 0.0 1236.0 0.0 5 | S15 f 39.0 26.0 0.0 0.0 1236.0 0.0 6 | C30 c 20.0 55.0 10.0 355.0 407.0 90.0 7 | C12 c 25.0 85.0 20.0 176.0 228.0 90.0 8 | C100 c 55.0 85.0 20.0 744.0 798.0 90.0 9 | C85 c 68.0 60.0 30.0 737.0 809.0 90.0 10 | C64 c 48.0 30.0 10.0 263.0 325.0 90.0 11 | 12 | Q Vehicle fuel tank capacity /77.75/ 13 | C Vehicle load capacity /200.0/ 14 | r fuel consumption rate /1.0/ 15 | g inverse refueling rate /3.47/ 16 | v average Velocity /1.0/ 17 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/c208C5.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 3390.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 3390.0 0.0 4 | S11 f 10.0 28.0 0.0 0.0 3390.0 0.0 5 | S14 f 27.0 10.0 0.0 0.0 3390.0 0.0 6 | C39 c 0.0 45.0 20.0 1642.0 2282.0 90.0 7 | C50 c 26.0 32.0 10.0 255.0 895.0 90.0 8 | C60 c 35.0 5.0 20.0 1152.0 1792.0 90.0 9 | C58 c 38.0 5.0 30.0 1245.0 1885.0 90.0 10 | C53 c 44.0 5.0 20.0 1431.0 2071.0 90.0 11 | 12 | Q Vehicle fuel tank capacity /77.75/ 13 | C Vehicle load capacity /700.0/ 14 | r fuel consumption rate /1.0/ 15 | g inverse refueling rate /3.47/ 16 | v average Velocity /1.0/ 17 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/r104C5.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 35.0 35.0 0.0 0.0 230.0 0.0 3 | S0 f 35.0 35.0 0.0 0.0 230.0 0.0 4 | S3 f 48.0 60.0 0.0 0.0 230.0 0.0 5 | S9 f 15.0 42.0 0.0 0.0 230.0 0.0 6 | C71 c 57.0 68.0 15.0 0.0 180.0 10.0 7 | C1 c 41.0 49.0 10.0 36.0 46.0 10.0 8 | C5 c 15.0 30.0 26.0 0.0 199.0 10.0 9 | C87 c 28.0 18.0 26.0 166.0 176.0 10.0 10 | C99 c 20.0 26.0 9.0 0.0 202.0 10.0 11 | 12 | Q Vehicle fuel tank capacity /60.63/ 13 | C Vehicle load capacity /200.0/ 14 | r fuel consumption rate /1.0/ 15 | g inverse refueling rate /0.49/ 16 | v average Velocity /1.0/ 17 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/r105C5.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 35.0 35.0 0.0 0.0 230.0 0.0 3 | S0 f 35.0 35.0 0.0 0.0 230.0 0.0 4 | S1 f 64.0 37.0 0.0 0.0 230.0 0.0 5 | S13 f 21.0 22.0 0.0 0.0 230.0 0.0 6 | C91 c 15.0 19.0 1.0 77.0 107.0 10.0 7 | C28 c 41.0 37.0 16.0 183.0 213.0 10.0 8 | C75 c 49.0 11.0 18.0 49.0 79.0 10.0 9 | C78 c 61.0 52.0 3.0 158.0 188.0 10.0 10 | C95 c 25.0 24.0 20.0 130.0 160.0 10.0 11 | 12 | Q Vehicle fuel tank capacity /60.63/ 13 | C Vehicle load capacity /200.0/ 14 | r fuel consumption rate /1.0/ 15 | g inverse refueling rate /0.49/ 16 | v average Velocity /1.0/ 17 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/r202C5.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 35.0 35.0 0.0 0.0 1000.0 0.0 3 | S0 f 35.0 35.0 0.0 0.0 1000.0 0.0 4 | S13 f 21.0 22.0 0.0 0.0 1000.0 0.0 5 | S15 f 34.0 16.0 0.0 0.0 1000.0 0.0 6 | C77 c 53.0 43.0 14.0 86.0 224.0 10.0 7 | C17 c 5.0 30.0 2.0 0.0 959.0 10.0 8 | C37 c 20.0 20.0 8.0 0.0 968.0 10.0 9 | C18 c 20.0 40.0 12.0 403.0 513.0 10.0 10 | C72 c 47.0 16.0 25.0 104.0 252.0 10.0 11 | 12 | Q Vehicle fuel tank capacity /60.63/ 13 | C Vehicle load capacity /1000.0/ 14 | r fuel consumption rate /1.0/ 15 | g inverse refueling rate /0.49/ 16 | v average Velocity /1.0/ 17 | -------------------------------------------------------------------------------- /EVRPTW/ACO/penality_instances/c101C5.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 1236.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 1236.0 0.0 4 | S5 f 31.0 84.0 0.0 0.0 1236.0 0.0 5 | S15 f 39.0 26.0 0.0 0.0 1236.0 0.0 6 | C30 c 20.0 55.0 10.0 355.0 407.0 90.0 7 | C12 c 25.0 85.0 20.0 176.0 228.0 90.0 8 | C100 c 55.0 85.0 20.0 744.0 798.0 90.0 9 | C85 c 68.0 60.0 30.0 737.0 809.0 90.0 10 | C64 c 48.0 30.0 10.0 263.0 325.0 90.0 11 | 12 | Q Vehicle fuel tank capacity /77.75/ 13 | C Vehicle load capacity /200.0/ 14 | r fuel consumption rate /1.0/ 15 | g inverse refueling rate /3.47/ 16 | v average Velocity /1.0/ 17 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/rc208C5.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 960.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 960.0 0.0 4 | S3 f 57.0 82.0 0.0 0.0 960.0 0.0 5 | S19 f 77.0 30.0 0.0 0.0 960.0 0.0 6 | C66 c 41.0 37.0 16.0 383.0 905.0 10.0 7 | C37 c 65.0 82.0 10.0 59.0 632.0 10.0 8 | C96 c 55.0 54.0 26.0 142.0 532.0 10.0 9 | C41 c 58.0 75.0 20.0 73.0 561.0 10.0 10 | C32 c 87.0 30.0 10.0 69.0 539.0 10.0 11 | 12 | Q Vehicle fuel tank capacity /77.75/ 13 | C Vehicle load capacity /1000.0/ 14 | r fuel consumption rate /1.0/ 15 | g inverse refueling rate /0.39/ 16 | v average Velocity /1.0/ 17 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/r203C5.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 35.0 35.0 0.0 0.0 1000.0 0.0 3 | S0 f 35.0 35.0 0.0 0.0 1000.0 0.0 4 | S1 f 64.0 37.0 0.0 0.0 1000.0 0.0 5 | S7 f 20.0 61.0 0.0 0.0 1000.0 0.0 6 | S9 f 15.0 42.0 0.0 0.0 1000.0 0.0 7 | C50 c 47.0 47.0 13.0 507.0 599.0 10.0 8 | C96 c 22.0 27.0 11.0 0.0 974.0 10.0 9 | C79 c 57.0 48.0 23.0 0.0 964.0 10.0 10 | C25 c 65.0 20.0 6.0 418.0 532.0 10.0 11 | C49 c 6.0 68.0 30.0 0.0 946.0 10.0 12 | 13 | Q Vehicle fuel tank capacity /60.63/ 14 | C Vehicle load capacity /1000.0/ 15 | r fuel consumption rate /1.0/ 16 | g inverse refueling rate /0.49/ 17 | v average Velocity /1.0/ 18 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/rc105C5.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 240.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 240.0 0.0 4 | S3 f 57.0 82.0 0.0 0.0 240.0 0.0 5 | S9 f 14.0 59.0 0.0 0.0 240.0 0.0 6 | S15 f 39.0 26.0 0.0 0.0 240.0 0.0 7 | C11 c 8.0 40.0 40.0 76.0 126.0 10.0 8 | C22 c 40.0 15.0 40.0 96.0 146.0 10.0 9 | C55 c 30.0 60.0 16.0 15.0 116.0 10.0 10 | C82 c 27.0 43.0 9.0 111.0 147.0 10.0 11 | C36 c 65.0 85.0 40.0 59.0 114.0 10.0 12 | 13 | Q Vehicle fuel tank capacity /77.75/ 14 | C Vehicle load capacity /200.0/ 15 | r fuel consumption rate /1.0/ 16 | g inverse refueling rate /0.39/ 17 | v average Velocity /1.0/ 18 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/c206C5.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 3390.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 3390.0 0.0 4 | S11 f 10.0 28.0 0.0 0.0 3390.0 0.0 5 | S15 f 39.0 26.0 0.0 0.0 3390.0 0.0 6 | S17 f 61.0 14.0 0.0 0.0 3390.0 0.0 7 | C35 c 5.0 35.0 10.0 1954.0 2536.0 90.0 8 | C53 c 44.0 5.0 20.0 1442.0 2060.0 90.0 9 | C44 c 32.0 20.0 10.0 693.0 1215.0 90.0 10 | C77 c 72.0 45.0 10.0 1419.0 1969.0 90.0 11 | C75 c 45.0 65.0 20.0 948.0 1570.0 90.0 12 | 13 | Q Vehicle fuel tank capacity /77.75/ 14 | C Vehicle load capacity /700.0/ 15 | r fuel consumption rate /1.0/ 16 | g inverse refueling rate /3.47/ 17 | v average Velocity /1.0/ 18 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/rc108C5.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 240.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 240.0 0.0 4 | S11 f 10.0 28.0 0.0 0.0 240.0 0.0 5 | S14 f 27.0 10.0 0.0 0.0 240.0 0.0 6 | S19 f 77.0 30.0 0.0 0.0 240.0 0.0 7 | C34 c 85.0 35.0 30.0 68.0 182.0 10.0 8 | C21 c 40.0 5.0 10.0 55.0 185.0 10.0 9 | C97 c 4.0 18.0 35.0 58.0 131.0 10.0 10 | C71 c 65.0 55.0 14.0 26.0 111.0 10.0 11 | C15 c 2.0 40.0 20.0 96.0 190.0 10.0 12 | 13 | Q Vehicle fuel tank capacity /77.75/ 14 | C Vehicle load capacity /200.0/ 15 | r fuel consumption rate /1.0/ 16 | g inverse refueling rate /0.39/ 17 | v average Velocity /1.0/ 18 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/rc204C5.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 960.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 960.0 0.0 4 | S9 f 14.0 59.0 0.0 0.0 960.0 0.0 5 | S13 f 22.0 34.0 0.0 0.0 960.0 0.0 6 | S15 f 39.0 26.0 0.0 0.0 960.0 0.0 7 | C23 c 38.0 5.0 30.0 0.0 904.0 10.0 8 | C19 c 42.0 10.0 40.0 0.0 909.0 10.0 9 | C49 c 42.0 12.0 10.0 0.0 911.0 10.0 10 | C4 c 20.0 80.0 40.0 712.0 832.0 10.0 11 | C81 c 49.0 58.0 10.0 0.0 937.0 10.0 12 | 13 | Q Vehicle fuel tank capacity /77.75/ 14 | C Vehicle load capacity /1000.0/ 15 | r fuel consumption rate /1.0/ 16 | g inverse refueling rate /0.39/ 17 | v average Velocity /1.0/ 18 | -------------------------------------------------------------------------------- /EVRPTW/tests/test_target.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from ACO.target import Node 3 | 4 | def test_node_creation(): 5 | # Test the constructor and attribute assignments. 6 | node = Node(1, "C20", 'c', 10, 20, 30, 40, 50, 60) 7 | assert node.idx == 1 8 | assert node.string_id == "C20" 9 | assert node.node_type == 'c' 10 | assert node.x == 10 11 | assert node.y == 20 12 | assert node.demand == 30 13 | assert node.ready_time == 40 14 | assert node.due_date == 50 15 | assert node.service_time == 60 16 | 17 | def test_is_depot(): 18 | # Test the is_depot method. 19 | depot_node = Node(0, "D0", 'd', 0, 0, 0, 0, 0, 0) 20 | assert depot_node.is_depot() is True 21 | assert depot_node.is_station() is False 22 | assert depot_node.is_customer() is False 23 | 24 | def test_is_station(): 25 | # Test the is_station method. 26 | station_node = Node(2, "S0", 'f', 5, 5, 0, 0, 0, 0) 27 | assert station_node.is_depot() is False 28 | assert station_node.is_station() is True 29 | assert station_node.is_customer() is False 30 | 31 | def test_is_customer(): 32 | # Test the is_customer method. 33 | customer_node = Node(1, "C20", 'c', 10, 20, 30, 40, 50, 60) 34 | assert customer_node.is_depot() is False 35 | assert customer_node.is_station() is False 36 | assert customer_node.is_customer() is True 37 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/c205C10.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 3390.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 3390.0 0.0 4 | S3 f 57.0 82.0 0.0 0.0 3390.0 0.0 5 | S15 f 39.0 26.0 0.0 0.0 3390.0 0.0 6 | C8 c 34.0 60.0 20.0 12.0 332.0 90.0 7 | C9 c 28.0 70.0 10.0 138.0 458.0 90.0 8 | C47 c 30.0 35.0 10.0 227.0 547.0 90.0 9 | C60 c 35.0 5.0 20.0 1312.0 1632.0 90.0 10 | C94 c 65.0 82.0 10.0 429.0 749.0 90.0 11 | C75 c 45.0 65.0 20.0 1099.0 1419.0 90.0 12 | C66 c 47.0 35.0 10.0 2633.0 2953.0 90.0 13 | C99 c 55.0 80.0 10.0 810.0 1130.0 90.0 14 | C56 c 40.0 5.0 30.0 1497.0 1817.0 90.0 15 | C15 c 20.0 80.0 40.0 331.0 651.0 90.0 16 | 17 | Q Vehicle fuel tank capacity /77.75/ 18 | C Vehicle load capacity /700.0/ 19 | r fuel consumption rate /1.0/ 20 | g inverse refueling rate /3.47/ 21 | v average Velocity /1.0/ 22 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/r103C10.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 35.0 35.0 0.0 0.0 230.0 0.0 3 | S0 f 35.0 35.0 0.0 0.0 230.0 0.0 4 | S9 f 15.0 42.0 0.0 0.0 230.0 0.0 5 | S13 f 21.0 22.0 0.0 0.0 230.0 0.0 6 | C91 c 15.0 19.0 1.0 87.0 97.0 10.0 7 | C84 c 11.0 31.0 7.0 96.0 106.0 10.0 8 | C53 c 37.0 31.0 14.0 0.0 215.0 10.0 9 | C18 c 20.0 40.0 12.0 0.0 204.0 10.0 10 | C27 c 35.0 40.0 16.0 173.0 183.0 10.0 11 | C81 c 55.0 54.0 26.0 0.0 192.0 10.0 12 | C83 c 14.0 37.0 11.0 0.0 198.0 10.0 13 | C45 c 6.0 38.0 16.0 0.0 190.0 10.0 14 | C26 c 45.0 30.0 17.0 0.0 208.0 10.0 15 | C16 c 10.0 20.0 19.0 71.0 81.0 10.0 16 | 17 | Q Vehicle fuel tank capacity /60.63/ 18 | C Vehicle load capacity /200.0/ 19 | r fuel consumption rate /1.0/ 20 | g inverse refueling rate /0.49/ 21 | v average Velocity /1.0/ 22 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/r102C10.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 35.0 35.0 0.0 0.0 230.0 0.0 3 | S0 f 35.0 35.0 0.0 0.0 230.0 0.0 4 | S5 f 28.0 62.0 0.0 0.0 230.0 0.0 5 | S17 f 51.0 7.0 0.0 0.0 230.0 0.0 6 | S18 f 63.0 12.0 0.0 0.0 230.0 0.0 7 | C31 c 31.0 52.0 27.0 25.0 35.0 10.0 8 | C23 c 55.0 5.0 29.0 97.0 107.0 10.0 9 | C67 c 67.0 5.0 25.0 0.0 176.0 10.0 10 | C60 c 17.0 34.0 3.0 0.0 201.0 10.0 11 | C88 c 26.0 52.0 9.0 166.0 176.0 10.0 12 | C77 c 53.0 43.0 14.0 150.0 160.0 10.0 13 | C20 c 45.0 65.0 9.0 77.0 87.0 10.0 14 | C99 c 20.0 26.0 9.0 138.0 148.0 10.0 15 | C12 c 50.0 35.0 19.0 177.0 187.0 10.0 16 | C21 c 45.0 20.0 11.0 0.0 201.0 10.0 17 | 18 | Q Vehicle fuel tank capacity /60.63/ 19 | C Vehicle load capacity /200.0/ 20 | r fuel consumption rate /1.0/ 21 | g inverse refueling rate /0.49/ 22 | v average Velocity /1.0/ 23 | -------------------------------------------------------------------------------- /EVRPTW/ACO/test_instances/r201C10.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 35.0 35.0 0.0 0.0 1000.0 0.0 3 | S0 f 35.0 35.0 0.0 0.0 1000.0 0.0 4 | S5 f 28.0 62.0 0.0 0.0 1000.0 0.0 5 | S13 f 21.0 22.0 0.0 0.0 1000.0 0.0 6 | S15 f 34.0 16.0 0.0 0.0 1000.0 0.0 7 | C77 c 53.0 43.0 14.0 86.0 224.0 10.0 8 | C50 c 47.0 47.0 13.0 507.0 599.0 10.0 9 | C18 c 20.0 40.0 12.0 403.0 513.0 10.0 10 | C28 c 41.0 37.0 16.0 357.0 495.0 10.0 11 | C32 c 35.0 69.0 23.0 501.0 621.0 10.0 12 | C31 c 31.0 52.0 27.0 591.0 809.0 10.0 13 | C84 c 11.0 31.0 7.0 314.0 448.0 10.0 14 | C72 c 47.0 16.0 25.0 104.0 252.0 10.0 15 | C94 c 26.0 27.0 27.0 534.0 642.0 10.0 16 | C100 c 18.0 18.0 17.0 118.0 148.0 10.0 17 | 18 | Q Vehicle fuel tank capacity /60.63/ 19 | C Vehicle load capacity /1000.0/ 20 | r fuel consumption rate /1.0/ 21 | g inverse refueling rate /0.49/ 22 | v average Velocity /1.0/ 23 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/rc205C10.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 960.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 960.0 0.0 4 | S1 f 77.0 52.0 0.0 0.0 960.0 0.0 5 | S3 f 57.0 82.0 0.0 0.0 960.0 0.0 6 | S11 f 10.0 28.0 0.0 0.0 960.0 0.0 7 | C96 c 55.0 54.0 26.0 319.0 355.0 10.0 8 | C43 c 55.0 85.0 20.0 39.0 490.0 10.0 9 | C87 c 12.0 24.0 13.0 441.0 441.0 10.0 10 | C100 c 31.0 67.0 3.0 528.0 528.0 10.0 11 | C15 c 2.0 40.0 20.0 607.0 739.0 10.0 12 | C58 c 15.0 10.0 20.0 198.0 406.0 10.0 13 | C31 c 88.0 35.0 20.0 71.0 689.0 10.0 14 | C82 c 27.0 43.0 9.0 488.0 650.0 10.0 15 | C93 c 61.0 52.0 3.0 350.0 404.0 10.0 16 | C6 c 18.0 75.0 20.0 142.0 494.0 10.0 17 | 18 | Q Vehicle fuel tank capacity /77.75/ 19 | C Vehicle load capacity /1000.0/ 20 | r fuel consumption rate /1.0/ 21 | g inverse refueling rate /0.39/ 22 | v average Velocity /1.0/ -------------------------------------------------------------------------------- /EVRPTW/ACO/penality_instances/c104C10.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 1236.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 1236.0 0.0 4 | S3 f 57.0 82.0 0.0 0.0 1236.0 0.0 5 | S13 f 22.0 34.0 0.0 0.0 1236.0 0.0 6 | S18 f 76.0 21.0 0.0 0.0 1236.0 0.0 7 | C72 c 53.0 30.0 10.0 0.0 1122.0 90.0 8 | C34 c 8.0 45.0 20.0 0.0 1113.0 90.0 9 | C80 c 85.0 25.0 10.0 0.0 1094.0 90.0 10 | C3 c 42.0 66.0 10.0 0.0 1129.0 90.0 11 | C88 c 65.0 60.0 30.0 0.0 1119.0 90.0 12 | C96 c 60.0 80.0 10.0 177.0 243.0 90.0 13 | C42 c 33.0 32.0 20.0 0.0 1126.0 90.0 14 | C22 c 28.0 52.0 20.0 0.0 1133.0 90.0 15 | C57 c 40.0 15.0 40.0 0.0 1111.0 90.0 16 | C48 c 28.0 30.0 10.0 0.0 1122.0 90.0 17 | 18 | Q Vehicle fuel tank capacity /77.75/ 19 | C Vehicle load capacity /200.0/ 20 | r fuel consumption rate /1.0/ 21 | g inverse refueling rate /3.47/ 22 | v average Velocity /1.0/ 23 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/c104C10.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 1236.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 1236.0 0.0 4 | S3 f 57.0 82.0 0.0 0.0 1236.0 0.0 5 | S13 f 22.0 34.0 0.0 0.0 1236.0 0.0 6 | S18 f 76.0 21.0 0.0 0.0 1236.0 0.0 7 | C72 c 53.0 30.0 10.0 0.0 1122.0 90.0 8 | C34 c 8.0 45.0 20.0 0.0 1113.0 90.0 9 | C80 c 85.0 25.0 10.0 0.0 1094.0 90.0 10 | C3 c 42.0 66.0 10.0 0.0 1129.0 90.0 11 | C88 c 65.0 60.0 30.0 0.0 1119.0 90.0 12 | C96 c 60.0 80.0 10.0 177.0 243.0 90.0 13 | C42 c 33.0 32.0 20.0 0.0 1126.0 90.0 14 | C22 c 28.0 52.0 20.0 0.0 1133.0 90.0 15 | C57 c 40.0 15.0 40.0 0.0 1111.0 90.0 16 | C48 c 28.0 30.0 10.0 0.0 1122.0 90.0 17 | 18 | Q Vehicle fuel tank capacity /77.75/ 19 | C Vehicle load capacity /200.0/ 20 | r fuel consumption rate /1.0/ 21 | g inverse refueling rate /3.47/ 22 | v average Velocity /1.0/ 23 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/r201C10.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 35.0 35.0 0.0 0.0 1000.0 0.0 3 | S0 f 35.0 35.0 0.0 0.0 1000.0 0.0 4 | S5 f 28.0 62.0 0.0 0.0 1000.0 0.0 5 | S13 f 21.0 22.0 0.0 0.0 1000.0 0.0 6 | S15 f 34.0 16.0 0.0 0.0 1000.0 0.0 7 | C77 c 53.0 43.0 14.0 86.0 224.0 10.0 8 | C50 c 47.0 47.0 13.0 507.0 599.0 10.0 9 | C18 c 20.0 40.0 12.0 403.0 513.0 10.0 10 | C28 c 41.0 37.0 16.0 357.0 495.0 10.0 11 | C32 c 35.0 69.0 23.0 501.0 621.0 10.0 12 | C31 c 31.0 52.0 27.0 591.0 809.0 10.0 13 | C84 c 11.0 31.0 7.0 314.0 448.0 10.0 14 | C72 c 47.0 16.0 25.0 104.0 252.0 10.0 15 | C94 c 26.0 27.0 27.0 534.0 642.0 10.0 16 | C100 c 18.0 18.0 17.0 118.0 148.0 10.0 17 | 18 | Q Vehicle fuel tank capacity /60.63/ 19 | C Vehicle load capacity /1000.0/ 20 | r fuel consumption rate /1.0/ 21 | g inverse refueling rate /0.49/ 22 | v average Velocity /1.0/ 23 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/rc102C10.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 240.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 240.0 0.0 4 | S1 f 77.0 52.0 0.0 0.0 240.0 0.0 5 | S15 f 39.0 26.0 0.0 0.0 240.0 0.0 6 | S19 f 77.0 30.0 0.0 0.0 240.0 0.0 7 | C70 c 35.0 69.0 23.0 83.0 113.0 10.0 8 | C57 c 30.0 25.0 23.0 27.0 57.0 10.0 9 | C49 c 42.0 12.0 10.0 85.0 115.0 10.0 10 | C45 c 20.0 82.0 10.0 53.0 83.0 10.0 11 | C54 c 55.0 60.0 16.0 170.0 200.0 10.0 12 | C92 c 53.0 43.0 14.0 140.0 170.0 10.0 13 | C26 c 95.0 30.0 30.0 121.0 151.0 10.0 14 | C11 c 8.0 40.0 40.0 86.0 116.0 10.0 15 | C53 c 20.0 50.0 5.0 0.0 210.0 10.0 16 | C44 c 55.0 82.0 10.0 36.0 66.0 10.0 17 | 18 | Q Vehicle fuel tank capacity /77.75/ 19 | C Vehicle load capacity /200.0/ 20 | r fuel consumption rate /1.0/ 21 | g inverse refueling rate /0.39/ 22 | v average Velocity /1.0/ 23 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/rc108C10.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 240.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 240.0 0.0 4 | S3 f 57.0 82.0 0.0 0.0 240.0 0.0 5 | S5 f 31.0 84.0 0.0 0.0 240.0 0.0 6 | S19 f 77.0 30.0 0.0 0.0 240.0 0.0 7 | C83 c 37.0 31.0 14.0 74.0 192.0 10.0 8 | C45 c 20.0 82.0 10.0 38.0 192.0 10.0 9 | C35 c 67.0 85.0 20.0 61.0 185.0 10.0 10 | C28 c 92.0 30.0 10.0 74.0 174.0 10.0 11 | C79 c 6.0 68.0 30.0 39.0 151.0 10.0 12 | C65 c 35.0 40.0 16.0 12.0 109.0 10.0 13 | C93 c 61.0 52.0 3.0 22.0 101.0 10.0 14 | C85 c 63.0 23.0 2.0 36.0 194.0 10.0 15 | C80 c 47.0 47.0 13.0 8.0 150.0 10.0 16 | C86 c 21.0 24.0 28.0 125.0 197.0 10.0 17 | 18 | Q Vehicle fuel tank capacity /77.75/ 19 | C Vehicle load capacity /200.0/ 20 | r fuel consumption rate /1.0/ 21 | g inverse refueling rate /0.39/ 22 | v average Velocity /1.0/ 23 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/rc201C10.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 960.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 960.0 0.0 4 | S3 f 57.0 82.0 0.0 0.0 960.0 0.0 5 | S9 f 14.0 59.0 0.0 0.0 960.0 0.0 6 | S15 f 39.0 26.0 0.0 0.0 960.0 0.0 7 | C98 c 26.0 52.0 9.0 218.0 338.0 10.0 8 | C14 c 5.0 45.0 10.0 198.0 318.0 10.0 9 | C81 c 49.0 58.0 10.0 784.0 904.0 10.0 10 | C88 c 24.0 58.0 19.0 561.0 681.0 10.0 11 | C62 c 65.0 35.0 3.0 138.0 258.0 10.0 12 | C43 c 55.0 85.0 20.0 102.0 222.0 10.0 13 | C93 c 61.0 52.0 3.0 317.0 437.0 10.0 14 | C22 c 40.0 15.0 40.0 621.0 741.0 10.0 15 | C54 c 55.0 60.0 16.0 557.0 677.0 10.0 16 | C79 c 6.0 68.0 30.0 39.0 159.0 10.0 17 | 18 | Q Vehicle fuel tank capacity /77.75/ 19 | C Vehicle load capacity /1000.0/ 20 | r fuel consumption rate /1.0/ 21 | g inverse refueling rate /0.39/ 22 | v average Velocity /1.0/ 23 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/c101C10.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 1236.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 1236.0 0.0 4 | S1 f 77.0 52.0 0.0 0.0 1236.0 0.0 5 | S3 f 57.0 82.0 0.0 0.0 1236.0 0.0 6 | S16 f 48.0 8.0 0.0 0.0 1236.0 0.0 7 | S20 f 93.0 43.0 0.0 0.0 1236.0 0.0 8 | C98 c 58.0 75.0 20.0 181.0 247.0 90.0 9 | C78 c 88.0 35.0 20.0 667.0 731.0 90.0 10 | C4 c 42.0 68.0 10.0 584.0 656.0 90.0 11 | C13 c 22.0 75.0 30.0 1042.0 1106.0 90.0 12 | C95 c 62.0 80.0 30.0 274.0 330.0 90.0 13 | C100 c 55.0 85.0 20.0 744.0 798.0 90.0 14 | C54 c 42.0 10.0 40.0 810.0 868.0 90.0 15 | C27 c 23.0 52.0 10.0 263.0 311.0 90.0 16 | C89 c 63.0 58.0 10.0 929.0 989.0 90.0 17 | C96 c 60.0 80.0 10.0 177.0 243.0 90.0 18 | 19 | Q Vehicle fuel tank capacity /77.75/ 20 | C Vehicle load capacity /200.0/ 21 | r fuel consumption rate /1.0/ 22 | g inverse refueling rate /3.47/ 23 | v average Velocity /1.0/ 24 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/c202C10.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 3390.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 3390.0 0.0 4 | S1 f 77.0 52.0 0.0 0.0 3390.0 0.0 5 | S7 f 21.0 83.0 0.0 0.0 3390.0 0.0 6 | S13 f 22.0 34.0 0.0 0.0 3390.0 0.0 7 | S15 f 39.0 26.0 0.0 0.0 3390.0 0.0 8 | C96 c 62.0 40.0 10.0 1808.0 1968.0 90.0 9 | C30 c 20.0 55.0 10.0 2737.0 2897.0 90.0 10 | C84 c 70.0 58.0 20.0 321.0 481.0 90.0 11 | C16 c 20.0 85.0 40.0 693.0 853.0 90.0 12 | C57 c 38.0 15.0 40.0 0.0 3264.0 90.0 13 | C8 c 34.0 60.0 20.0 12.0 172.0 90.0 14 | C24 c 25.0 50.0 10.0 2925.0 3085.0 90.0 15 | C25 c 22.0 66.0 40.0 1078.0 1238.0 90.0 16 | C10 c 35.0 66.0 10.0 28.0 188.0 90.0 17 | C6 c 16.0 42.0 20.0 2544.0 2704.0 90.0 18 | 19 | Q Vehicle fuel tank capacity /77.75/ 20 | C Vehicle load capacity /700.0/ 21 | r fuel consumption rate /1.0/ 22 | g inverse refueling rate /3.47/ 23 | v average Velocity /1.0/ 24 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/r203C10.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 35.0 35.0 0.0 0.0 1000.0 0.0 3 | S0 f 35.0 35.0 0.0 0.0 1000.0 0.0 4 | S4 f 59.0 67.0 0.0 0.0 1000.0 0.0 5 | S7 f 20.0 61.0 0.0 0.0 1000.0 0.0 6 | S9 f 15.0 42.0 0.0 0.0 1000.0 0.0 7 | S13 f 21.0 22.0 0.0 0.0 1000.0 0.0 8 | C84 c 11.0 31.0 7.0 0.0 965.0 10.0 9 | C11 c 20.0 65.0 12.0 0.0 956.0 10.0 10 | C57 c 32.0 12.0 7.0 0.0 966.0 10.0 11 | C78 c 61.0 52.0 3.0 648.0 826.0 10.0 12 | C69 c 37.0 47.0 6.0 0.0 977.0 10.0 13 | C5 c 15.0 30.0 26.0 21.0 182.0 10.0 14 | C61 c 12.0 24.0 13.0 329.0 551.0 10.0 15 | C75 c 49.0 11.0 18.0 0.0 962.0 10.0 16 | C71 c 57.0 68.0 15.0 0.0 950.0 10.0 17 | C17 c 5.0 30.0 2.0 0.0 959.0 10.0 18 | 19 | Q Vehicle fuel tank capacity /60.63/ 20 | C Vehicle load capacity /1000.0/ 21 | r fuel consumption rate /1.0/ 22 | g inverse refueling rate /0.49/ 23 | v average Velocity /1.0/ 24 | -------------------------------------------------------------------------------- /EVRPTW/ACO/penality_instances/c101C10.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 1236.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 1236.0 0.0 4 | S1 f 77.0 52.0 0.0 0.0 1236.0 0.0 5 | S3 f 57.0 82.0 0.0 0.0 1236.0 0.0 6 | S16 f 48.0 8.0 0.0 0.0 1236.0 0.0 7 | S20 f 93.0 43.0 0.0 0.0 1236.0 0.0 8 | C98 c 58.0 75.0 20.0 181.0 247.0 90.0 9 | C78 c 88.0 35.0 20.0 667.0 731.0 90.0 10 | C4 c 42.0 68.0 10.0 584.0 656.0 90.0 11 | C13 c 22.0 75.0 30.0 1042.0 1106.0 90.0 12 | C95 c 62.0 80.0 30.0 274.0 330.0 90.0 13 | C100 c 55.0 85.0 20.0 744.0 798.0 90.0 14 | C54 c 42.0 10.0 40.0 810.0 868.0 90.0 15 | C27 c 23.0 52.0 10.0 263.0 311.0 90.0 16 | C89 c 63.0 58.0 10.0 929.0 989.0 90.0 17 | C96 c 60.0 80.0 10.0 177.0 243.0 90.0 18 | 19 | Q Vehicle fuel tank capacity /77.75/ 20 | C Vehicle load capacity /200.0/ 21 | r fuel consumption rate /1.0/ 22 | g inverse refueling rate /3.47/ 23 | v average Velocity /1.0/ 24 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/c106C15.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 1236.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 1236.0 0.0 4 | S15 f 39.0 26.0 0.0 0.0 1236.0 0.0 5 | S19 f 77.0 30.0 0.0 0.0 1236.0 0.0 6 | C44 c 32.0 30.0 10.0 264.0 448.0 90.0 7 | C89 c 63.0 58.0 10.0 877.0 1041.0 90.0 8 | C91 c 60.0 60.0 10.0 733.0 1123.0 90.0 9 | C9 c 38.0 70.0 10.0 760.0 1125.0 90.0 10 | C64 c 48.0 30.0 10.0 204.0 384.0 90.0 11 | C27 c 23.0 52.0 10.0 263.0 311.0 90.0 12 | C20 c 30.0 50.0 10.0 10.0 168.0 90.0 13 | C51 c 25.0 30.0 10.0 747.0 965.0 90.0 14 | C45 c 30.0 30.0 10.0 393.0 503.0 90.0 15 | C68 c 45.0 30.0 10.0 277.0 497.0 90.0 16 | C66 c 47.0 35.0 10.0 957.0 1129.0 90.0 17 | C1 c 45.0 68.0 10.0 19.0 206.0 90.0 18 | C78 c 88.0 35.0 20.0 597.0 801.0 90.0 19 | C84 c 70.0 58.0 20.0 599.0 761.0 90.0 20 | C47 c 30.0 35.0 10.0 664.0 788.0 90.0 21 | 22 | Q Vehicle fuel tank capacity /77.75/ 23 | C Vehicle load capacity /200.0/ 24 | r fuel consumption rate /1.0/ 25 | g inverse refueling rate /3.47/ 26 | v average Velocity /1.0/ 27 | -------------------------------------------------------------------------------- /EVRPTW/ACO/penality_instances/c106C15.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 1236.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 1236.0 0.0 4 | S15 f 39.0 26.0 0.0 0.0 1236.0 0.0 5 | S19 f 77.0 30.0 0.0 0.0 1236.0 0.0 6 | C44 c 32.0 30.0 10.0 264.0 448.0 90.0 7 | C89 c 63.0 58.0 10.0 877.0 1041.0 90.0 8 | C91 c 60.0 60.0 10.0 733.0 1123.0 90.0 9 | C9 c 38.0 70.0 10.0 760.0 1125.0 90.0 10 | C64 c 48.0 30.0 10.0 204.0 384.0 90.0 11 | C27 c 23.0 52.0 10.0 263.0 311.0 90.0 12 | C20 c 30.0 50.0 10.0 10.0 168.0 90.0 13 | C51 c 25.0 30.0 10.0 747.0 965.0 90.0 14 | C45 c 30.0 30.0 10.0 393.0 503.0 90.0 15 | C68 c 45.0 30.0 10.0 277.0 497.0 90.0 16 | C66 c 47.0 35.0 10.0 957.0 1129.0 90.0 17 | C1 c 45.0 68.0 10.0 19.0 206.0 90.0 18 | C78 c 88.0 35.0 20.0 597.0 801.0 90.0 19 | C84 c 70.0 58.0 20.0 599.0 761.0 90.0 20 | C47 c 30.0 35.0 10.0 664.0 788.0 90.0 21 | 22 | Q Vehicle fuel tank capacity /77.75/ 23 | C Vehicle load capacity /200.0/ 24 | r fuel consumption rate /1.0/ 25 | g inverse refueling rate /3.47/ 26 | v average Velocity /1.0/ 27 | -------------------------------------------------------------------------------- /EVRPTW/ACO/utils/plot/plot_penalty.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | 5 | def plot_penalty_improvement_per_run(directory): 6 | """ 7 | Plots penalty over iterations for each run in the CSV files in the given directory, 8 | if there is a non-zero penalty value. Saves the plots in a directory named 'plot_penalty' 9 | within the same location as the CSV file. Uses red line for the plots. 10 | """ 11 | for root, dirs, files in os.walk(directory): 12 | for file in files: 13 | if file.endswith('fitness_result.csv'): 14 | file_path = os.path.join(root, file) 15 | df = pd.read_csv(file_path) 16 | 17 | 18 | # Remove last line and verify penalty values 19 | df = df.iloc[:-1] 20 | if df['penalty'].eq(0).all(): 21 | continue 22 | 23 | # Create folder for save graphs 24 | graphs_directory = os.path.join(root, "plot_penalty_fix") 25 | if not os.path.exists(graphs_directory): 26 | os.makedirs(graphs_directory) 27 | 28 | graph_title = os.path.basename(root) 29 | 30 | for run, run_df in df.groupby('run'): 31 | if not run_df.empty: 32 | fig, ax = plt.subplots(figsize=(10, 6)) 33 | ax.plot(run_df['improvement_iteration'], run_df['penalty'], marker='o', color='red') # Red line 34 | ax.set_title(f'Penalty Improvement - {graph_title} - Run {run}') 35 | ax.set_xlabel('Improvement Iteration') 36 | ax.set_ylabel('Penalty') 37 | ax.grid(True) 38 | 39 | plt.savefig(os.path.join(graphs_directory, f'{file[:-4]}_Run_{run}.png')) 40 | plt.close(fig) 41 | 42 | if __name__ == "__main__": 43 | results_directory = '../../results/' 44 | plot_penalty_improvement_per_run(results_directory) 45 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/c208C15.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 3390.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 3390.0 0.0 4 | S1 f 77.0 52.0 0.0 0.0 3390.0 0.0 5 | S14 f 27.0 10.0 0.0 0.0 3390.0 0.0 6 | S19 f 77.0 30.0 0.0 0.0 3390.0 0.0 7 | C24 c 25.0 50.0 10.0 2645.0 3285.0 90.0 8 | C20 c 30.0 50.0 10.0 2650.0 3290.0 90.0 9 | C98 c 58.0 75.0 20.0 79.0 719.0 90.0 10 | C75 c 45.0 65.0 20.0 939.0 1579.0 90.0 11 | C79 c 87.0 30.0 10.0 1082.0 1722.0 90.0 12 | C88 c 65.0 60.0 30.0 27.0 667.0 90.0 13 | C31 c 10.0 35.0 20.0 2020.0 2660.0 90.0 14 | C82 c 75.0 55.0 20.0 268.0 908.0 90.0 15 | C59 c 38.0 10.0 10.0 1056.0 1696.0 90.0 16 | C22 c 28.0 52.0 20.0 2647.0 3287.0 90.0 17 | C52 c 25.0 35.0 10.0 162.0 802.0 90.0 18 | C73 c 92.0 30.0 10.0 888.0 1528.0 90.0 19 | C43 c 33.0 35.0 10.0 17.0 657.0 90.0 20 | C85 c 86.0 46.0 30.0 503.0 1143.0 90.0 21 | C7 c 58.0 70.0 20.0 27.0 667.0 90.0 22 | 23 | Q Vehicle fuel tank capacity /77.75/ 24 | C Vehicle load capacity /700.0/ 25 | r fuel consumption rate /1.0/ 26 | g inverse refueling rate /3.47/ 27 | v average Velocity /1.0/ 28 | -------------------------------------------------------------------------------- /EVRPTW/ACO/test_instances/rc108C15.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 240.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 240.0 0.0 4 | S5 f 31.0 84.0 0.0 0.0 240.0 0.0 5 | S15 f 39.0 26.0 0.0 0.0 240.0 0.0 6 | S17 f 61.0 14.0 0.0 0.0 240.0 0.0 7 | S19 f 77.0 30.0 0.0 0.0 240.0 0.0 8 | C6 c 18.0 75.0 20.0 53.0 187.0 10.0 9 | C10 c 10.0 40.0 30.0 54.0 154.0 10.0 10 | C41 c 58.0 75.0 20.0 66.0 184.0 10.0 11 | C54 c 55.0 60.0 16.0 100.0 211.0 10.0 12 | C45 c 20.0 82.0 10.0 38.0 192.0 10.0 13 | C46 c 18.0 80.0 10.0 70.0 192.0 10.0 14 | C19 c 42.0 10.0 40.0 64.0 182.0 10.0 15 | C25 c 35.0 5.0 20.0 79.0 184.0 10.0 16 | C23 c 38.0 5.0 30.0 55.0 184.0 10.0 17 | C83 c 37.0 31.0 14.0 74.0 192.0 10.0 18 | C53 c 20.0 50.0 5.0 20.0 136.0 10.0 19 | C40 c 60.0 85.0 30.0 96.0 189.0 10.0 20 | C33 c 85.0 25.0 10.0 68.0 178.0 10.0 21 | C63 c 65.0 20.0 6.0 60.0 190.0 10.0 22 | C66 c 41.0 37.0 16.0 14.0 146.0 10.0 23 | 24 | Q Vehicle fuel tank capacity /77.75/ 25 | C Vehicle load capacity /200.0/ 26 | r fuel consumption rate /1.0/ 27 | g inverse refueling rate /0.39/ 28 | v average Velocity /1.0/ 29 | -------------------------------------------------------------------------------- /EVRPTW/ACO/test_instances/c202C15.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 3390.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 3390.0 0.0 4 | S1 f 77.0 52.0 0.0 0.0 3390.0 0.0 5 | S5 f 31.0 84.0 0.0 0.0 3390.0 0.0 6 | S15 f 39.0 26.0 0.0 0.0 3390.0 0.0 7 | S19 f 77.0 30.0 0.0 0.0 3390.0 0.0 8 | C17 c 18.0 75.0 20.0 0.0 3266.0 90.0 9 | C82 c 75.0 55.0 20.0 508.0 668.0 90.0 10 | C43 c 33.0 35.0 10.0 27.0 187.0 90.0 11 | C41 c 35.0 32.0 10.0 0.0 3281.0 90.0 12 | C27 c 23.0 52.0 10.0 2832.0 2992.0 90.0 13 | C99 c 55.0 80.0 10.0 890.0 1050.0 90.0 14 | C3 c 62.0 69.0 10.0 130.0 290.0 90.0 15 | C63 c 50.0 40.0 50.0 1910.0 2070.0 90.0 16 | C58 c 38.0 5.0 30.0 0.0 3254.0 90.0 17 | C73 c 92.0 30.0 10.0 0.0 3244.0 90.0 18 | C87 c 64.0 46.0 20.0 0.0 3275.0 90.0 19 | C10 c 35.0 66.0 10.0 28.0 188.0 90.0 20 | C51 c 25.0 30.0 10.0 587.0 747.0 90.0 21 | C23 c 14.0 66.0 10.0 1176.0 1336.0 90.0 22 | C76 c 90.0 35.0 10.0 845.0 1005.0 90.0 23 | 24 | Q Vehicle fuel tank capacity /77.75/ 25 | C Vehicle load capacity /700.0/ 26 | r fuel consumption rate /1.0/ 27 | g inverse refueling rate /3.47/ 28 | v average Velocity /1.0/ 29 | -------------------------------------------------------------------------------- /EVRPTW/ACO/test_instances/r209C15.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 35.0 35.0 0.0 0.0 1000.0 0.0 3 | S0 f 35.0 35.0 0.0 0.0 1000.0 0.0 4 | S1 f 64.0 37.0 0.0 0.0 1000.0 0.0 5 | S3 f 48.0 60.0 0.0 0.0 1000.0 0.0 6 | S7 f 20.0 61.0 0.0 0.0 1000.0 0.0 7 | S13 f 21.0 22.0 0.0 0.0 1000.0 0.0 8 | C47 c 8.0 56.0 27.0 371.0 819.0 10.0 9 | C87 c 28.0 18.0 26.0 535.0 563.0 10.0 10 | C9 c 55.0 60.0 16.0 395.0 835.0 10.0 11 | C88 c 26.0 52.0 9.0 165.0 617.0 10.0 12 | C46 c 2.0 48.0 1.0 46.0 460.0 10.0 13 | C27 c 35.0 40.0 16.0 97.0 679.0 10.0 14 | C10 c 30.0 60.0 16.0 489.0 787.0 10.0 15 | C70 c 37.0 56.0 5.0 22.0 286.0 10.0 16 | C30 c 40.0 60.0 21.0 422.0 726.0 10.0 17 | C5 c 15.0 30.0 26.0 21.0 535.0 10.0 18 | C22 c 45.0 10.0 18.0 607.0 963.0 10.0 19 | C79 c 57.0 48.0 23.0 26.0 389.0 10.0 20 | C4 c 55.0 20.0 19.0 701.0 883.0 10.0 21 | C63 c 27.0 69.0 10.0 360.0 890.0 10.0 22 | C34 c 65.0 55.0 14.0 116.0 502.0 10.0 23 | 24 | Q Vehicle fuel tank capacity /60.63/ 25 | C Vehicle load capacity /1000.0/ 26 | r fuel consumption rate /1.0/ 27 | g inverse refueling rate /0.49/ 28 | v average Velocity /1.0/ 29 | -------------------------------------------------------------------------------- /EVRPTW/ACO/test_instances/rc202C15.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 960.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 960.0 0.0 4 | S3 f 57.0 82.0 0.0 0.0 960.0 0.0 5 | S7 f 21.0 83.0 0.0 0.0 960.0 0.0 6 | S13 f 22.0 34.0 0.0 0.0 960.0 0.0 7 | S16 f 48.0 8.0 0.0 0.0 960.0 0.0 8 | C95 c 56.0 37.0 6.0 529.0 649.0 10.0 9 | C11 c 8.0 40.0 40.0 401.0 521.0 10.0 10 | C50 c 72.0 35.0 30.0 736.0 856.0 10.0 11 | C90 c 37.0 47.0 6.0 80.0 200.0 10.0 12 | C18 c 44.0 5.0 20.0 0.0 904.0 10.0 13 | C72 c 63.0 65.0 8.0 331.0 451.0 10.0 14 | C45 c 20.0 82.0 10.0 646.0 766.0 10.0 15 | C43 c 55.0 85.0 20.0 102.0 222.0 10.0 16 | C1 c 25.0 85.0 20.0 300.0 420.0 10.0 17 | C47 c 2.0 45.0 10.0 0.0 911.0 10.0 18 | C37 c 65.0 82.0 10.0 280.0 400.0 10.0 19 | C57 c 30.0 25.0 23.0 226.0 346.0 10.0 20 | C64 c 45.0 30.0 17.0 628.0 748.0 10.0 21 | C52 c 25.0 30.0 3.0 553.0 673.0 10.0 22 | C46 c 18.0 80.0 10.0 792.0 912.0 10.0 23 | 24 | Q Vehicle fuel tank capacity /77.75/ 25 | C Vehicle load capacity /1000.0/ 26 | r fuel consumption rate /1.0/ 27 | g inverse refueling rate /0.39/ 28 | v average Velocity /1.0/ 29 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/c202C15.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 3390.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 3390.0 0.0 4 | S1 f 77.0 52.0 0.0 0.0 3390.0 0.0 5 | S5 f 31.0 84.0 0.0 0.0 3390.0 0.0 6 | S15 f 39.0 26.0 0.0 0.0 3390.0 0.0 7 | S19 f 77.0 30.0 0.0 0.0 3390.0 0.0 8 | C17 c 18.0 75.0 20.0 0.0 3266.0 90.0 9 | C82 c 75.0 55.0 20.0 508.0 668.0 90.0 10 | C43 c 33.0 35.0 10.0 27.0 187.0 90.0 11 | C41 c 35.0 32.0 10.0 0.0 3281.0 90.0 12 | C27 c 23.0 52.0 10.0 2832.0 2992.0 90.0 13 | C99 c 55.0 80.0 10.0 890.0 1050.0 90.0 14 | C3 c 62.0 69.0 10.0 130.0 290.0 90.0 15 | C63 c 50.0 40.0 50.0 1910.0 2070.0 90.0 16 | C58 c 38.0 5.0 30.0 0.0 3254.0 90.0 17 | C73 c 92.0 30.0 10.0 0.0 3244.0 90.0 18 | C87 c 64.0 46.0 20.0 0.0 3275.0 90.0 19 | C10 c 35.0 66.0 10.0 28.0 188.0 90.0 20 | C51 c 25.0 30.0 10.0 587.0 747.0 90.0 21 | C23 c 14.0 66.0 10.0 1176.0 1336.0 90.0 22 | C76 c 90.0 35.0 10.0 845.0 1005.0 90.0 23 | 24 | Q Vehicle fuel tank capacity /77.75/ 25 | C Vehicle load capacity /700.0/ 26 | r fuel consumption rate /1.0/ 27 | g inverse refueling rate /3.47/ 28 | v average Velocity /1.0/ 29 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/rc108C15.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 240.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 240.0 0.0 4 | S5 f 31.0 84.0 0.0 0.0 240.0 0.0 5 | S15 f 39.0 26.0 0.0 0.0 240.0 0.0 6 | S17 f 61.0 14.0 0.0 0.0 240.0 0.0 7 | S19 f 77.0 30.0 0.0 0.0 240.0 0.0 8 | C6 c 18.0 75.0 20.0 53.0 187.0 10.0 9 | C10 c 10.0 40.0 30.0 54.0 154.0 10.0 10 | C41 c 58.0 75.0 20.0 66.0 184.0 10.0 11 | C54 c 55.0 60.0 16.0 100.0 211.0 10.0 12 | C45 c 20.0 82.0 10.0 38.0 192.0 10.0 13 | C46 c 18.0 80.0 10.0 70.0 192.0 10.0 14 | C19 c 42.0 10.0 40.0 64.0 182.0 10.0 15 | C25 c 35.0 5.0 20.0 79.0 184.0 10.0 16 | C23 c 38.0 5.0 30.0 55.0 184.0 10.0 17 | C83 c 37.0 31.0 14.0 74.0 192.0 10.0 18 | C53 c 20.0 50.0 5.0 20.0 136.0 10.0 19 | C40 c 60.0 85.0 30.0 96.0 189.0 10.0 20 | C33 c 85.0 25.0 10.0 68.0 178.0 10.0 21 | C63 c 65.0 20.0 6.0 60.0 190.0 10.0 22 | C66 c 41.0 37.0 16.0 14.0 146.0 10.0 23 | 24 | Q Vehicle fuel tank capacity /77.75/ 25 | C Vehicle load capacity /200.0/ 26 | r fuel consumption rate /1.0/ 27 | g inverse refueling rate /0.39/ 28 | v average Velocity /1.0/ 29 | -------------------------------------------------------------------------------- /EVRPTW/ACO/test_instances/rc103C15.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 240.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 240.0 0.0 4 | S3 f 57.0 82.0 0.0 0.0 240.0 0.0 5 | S7 f 21.0 83.0 0.0 0.0 240.0 0.0 6 | S11 f 10.0 28.0 0.0 0.0 240.0 0.0 7 | S19 f 77.0 30.0 0.0 0.0 240.0 0.0 8 | C33 c 85.0 25.0 10.0 0.0 178.0 10.0 9 | C84 c 57.0 29.0 18.0 73.0 103.0 10.0 10 | C15 c 2.0 40.0 20.0 160.0 190.0 10.0 11 | C6 c 18.0 75.0 20.0 0.0 196.0 10.0 12 | C90 c 37.0 47.0 6.0 145.0 175.0 10.0 13 | C62 c 65.0 35.0 3.0 0.0 200.0 10.0 14 | C8 c 15.0 80.0 10.0 125.0 155.0 10.0 15 | C39 c 60.0 80.0 10.0 100.0 130.0 10.0 16 | C34 c 85.0 35.0 30.0 0.0 182.0 10.0 17 | C65 c 35.0 40.0 16.0 31.0 61.0 10.0 18 | C70 c 35.0 69.0 23.0 0.0 210.0 10.0 19 | C72 c 63.0 65.0 8.0 106.0 136.0 10.0 20 | C87 c 12.0 24.0 13.0 58.0 88.0 10.0 21 | C100 c 31.0 67.0 3.0 0.0 210.0 10.0 22 | C83 c 37.0 31.0 14.0 0.0 210.0 10.0 23 | 24 | Q Vehicle fuel tank capacity /77.75/ 25 | C Vehicle load capacity /200.0/ 26 | r fuel consumption rate /1.0/ 27 | g inverse refueling rate /0.39/ 28 | v average Velocity /1.0/ 29 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/r209C15.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 35.0 35.0 0.0 0.0 1000.0 0.0 3 | S0 f 35.0 35.0 0.0 0.0 1000.0 0.0 4 | S1 f 64.0 37.0 0.0 0.0 1000.0 0.0 5 | S3 f 48.0 60.0 0.0 0.0 1000.0 0.0 6 | S7 f 20.0 61.0 0.0 0.0 1000.0 0.0 7 | S13 f 21.0 22.0 0.0 0.0 1000.0 0.0 8 | C47 c 8.0 56.0 27.0 371.0 819.0 10.0 9 | C87 c 28.0 18.0 26.0 535.0 563.0 10.0 10 | C9 c 55.0 60.0 16.0 395.0 835.0 10.0 11 | C88 c 26.0 52.0 9.0 165.0 617.0 10.0 12 | C46 c 2.0 48.0 1.0 46.0 460.0 10.0 13 | C27 c 35.0 40.0 16.0 97.0 679.0 10.0 14 | C10 c 30.0 60.0 16.0 489.0 787.0 10.0 15 | C70 c 37.0 56.0 5.0 22.0 286.0 10.0 16 | C30 c 40.0 60.0 21.0 422.0 726.0 10.0 17 | C5 c 15.0 30.0 26.0 21.0 535.0 10.0 18 | C22 c 45.0 10.0 18.0 607.0 963.0 10.0 19 | C79 c 57.0 48.0 23.0 26.0 389.0 10.0 20 | C4 c 55.0 20.0 19.0 701.0 883.0 10.0 21 | C63 c 27.0 69.0 10.0 360.0 890.0 10.0 22 | C34 c 65.0 55.0 14.0 116.0 502.0 10.0 23 | 24 | Q Vehicle fuel tank capacity /60.63/ 25 | C Vehicle load capacity /1000.0/ 26 | r fuel consumption rate /1.0/ 27 | g inverse refueling rate /0.49/ 28 | v average Velocity /1.0/ 29 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/rc202C15.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 960.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 960.0 0.0 4 | S3 f 57.0 82.0 0.0 0.0 960.0 0.0 5 | S7 f 21.0 83.0 0.0 0.0 960.0 0.0 6 | S13 f 22.0 34.0 0.0 0.0 960.0 0.0 7 | S16 f 48.0 8.0 0.0 0.0 960.0 0.0 8 | C95 c 56.0 37.0 6.0 529.0 649.0 10.0 9 | C11 c 8.0 40.0 40.0 401.0 521.0 10.0 10 | C50 c 72.0 35.0 30.0 736.0 856.0 10.0 11 | C90 c 37.0 47.0 6.0 80.0 200.0 10.0 12 | C18 c 44.0 5.0 20.0 0.0 904.0 10.0 13 | C72 c 63.0 65.0 8.0 331.0 451.0 10.0 14 | C45 c 20.0 82.0 10.0 646.0 766.0 10.0 15 | C43 c 55.0 85.0 20.0 102.0 222.0 10.0 16 | C1 c 25.0 85.0 20.0 300.0 420.0 10.0 17 | C47 c 2.0 45.0 10.0 0.0 911.0 10.0 18 | C37 c 65.0 82.0 10.0 280.0 400.0 10.0 19 | C57 c 30.0 25.0 23.0 226.0 346.0 10.0 20 | C64 c 45.0 30.0 17.0 628.0 748.0 10.0 21 | C52 c 25.0 30.0 3.0 553.0 673.0 10.0 22 | C46 c 18.0 80.0 10.0 792.0 912.0 10.0 23 | 24 | Q Vehicle fuel tank capacity /77.75/ 25 | C Vehicle load capacity /1000.0/ 26 | r fuel consumption rate /1.0/ 27 | g inverse refueling rate /0.39/ 28 | v average Velocity /1.0/ 29 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/rc103C15.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 240.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 240.0 0.0 4 | S3 f 57.0 82.0 0.0 0.0 240.0 0.0 5 | S7 f 21.0 83.0 0.0 0.0 240.0 0.0 6 | S11 f 10.0 28.0 0.0 0.0 240.0 0.0 7 | S19 f 77.0 30.0 0.0 0.0 240.0 0.0 8 | C33 c 85.0 25.0 10.0 0.0 178.0 10.0 9 | C84 c 57.0 29.0 18.0 73.0 103.0 10.0 10 | C15 c 2.0 40.0 20.0 160.0 190.0 10.0 11 | C6 c 18.0 75.0 20.0 0.0 196.0 10.0 12 | C90 c 37.0 47.0 6.0 145.0 175.0 10.0 13 | C62 c 65.0 35.0 3.0 0.0 200.0 10.0 14 | C8 c 15.0 80.0 10.0 125.0 155.0 10.0 15 | C39 c 60.0 80.0 10.0 100.0 130.0 10.0 16 | C34 c 85.0 35.0 30.0 0.0 182.0 10.0 17 | C65 c 35.0 40.0 16.0 31.0 61.0 10.0 18 | C70 c 35.0 69.0 23.0 0.0 210.0 10.0 19 | C72 c 63.0 65.0 8.0 106.0 136.0 10.0 20 | C87 c 12.0 24.0 13.0 58.0 88.0 10.0 21 | C100 c 31.0 67.0 3.0 0.0 210.0 10.0 22 | C83 c 37.0 31.0 14.0 0.0 210.0 10.0 23 | 24 | Q Vehicle fuel tank capacity /77.75/ 25 | C Vehicle load capacity /200.0/ 26 | r fuel consumption rate /1.0/ 27 | g inverse refueling rate /0.39/ 28 | v average Velocity /1.0/ 29 | -------------------------------------------------------------------------------- /EVRPTW/ACO/test_instances/r105C15.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 35.0 35.0 0.0 0.0 230.0 0.0 3 | S0 f 35.0 35.0 0.0 0.0 230.0 0.0 4 | S1 f 64.0 37.0 0.0 0.0 230.0 0.0 5 | S3 f 48.0 60.0 0.0 0.0 230.0 0.0 6 | S13 f 21.0 22.0 0.0 0.0 230.0 0.0 7 | S17 f 51.0 7.0 0.0 0.0 230.0 0.0 8 | S19 f 64.0 19.0 0.0 0.0 230.0 0.0 9 | C23 c 55.0 5.0 29.0 87.0 117.0 10.0 10 | C57 c 32.0 12.0 7.0 166.0 196.0 10.0 11 | C50 c 47.0 47.0 13.0 108.0 138.0 10.0 12 | C69 c 37.0 47.0 6.0 141.0 171.0 10.0 13 | C85 c 16.0 22.0 41.0 83.0 113.0 10.0 14 | C25 c 65.0 20.0 6.0 156.0 186.0 10.0 15 | C56 c 53.0 12.0 6.0 48.0 78.0 10.0 16 | C5 c 15.0 30.0 26.0 153.0 183.0 10.0 17 | C70 c 37.0 56.0 5.0 39.0 69.0 10.0 18 | C28 c 41.0 37.0 16.0 183.0 213.0 10.0 19 | C29 c 64.0 42.0 9.0 107.0 137.0 10.0 20 | C8 c 10.0 43.0 9.0 27.0 57.0 10.0 21 | C9 c 55.0 60.0 16.0 72.0 102.0 10.0 22 | C95 c 25.0 24.0 20.0 130.0 160.0 10.0 23 | C17 c 5.0 30.0 2.0 77.0 107.0 10.0 24 | 25 | Q Vehicle fuel tank capacity /60.63/ 26 | C Vehicle load capacity /200.0/ 27 | r fuel consumption rate /1.0/ 28 | g inverse refueling rate /0.49/ 29 | v average Velocity /1.0/ 30 | -------------------------------------------------------------------------------- /EVRPTW/ACO/test_instances/r202C15.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 35.0 35.0 0.0 0.0 1000.0 0.0 3 | S0 f 35.0 35.0 0.0 0.0 1000.0 0.0 4 | S5 f 28.0 62.0 0.0 0.0 1000.0 0.0 5 | S9 f 15.0 42.0 0.0 0.0 1000.0 0.0 6 | S13 f 21.0 22.0 0.0 0.0 1000.0 0.0 7 | S15 f 34.0 16.0 0.0 0.0 1000.0 0.0 8 | S19 f 64.0 19.0 0.0 0.0 1000.0 0.0 9 | C79 c 57.0 48.0 23.0 0.0 964.0 10.0 10 | C44 c 11.0 14.0 18.0 42.0 178.0 10.0 11 | C23 c 55.0 5.0 29.0 53.0 233.0 10.0 12 | C38 c 5.0 5.0 16.0 0.0 947.0 10.0 13 | C25 c 65.0 20.0 6.0 418.0 532.0 10.0 14 | C41 c 42.0 7.0 5.0 782.0 912.0 10.0 15 | C70 c 37.0 56.0 5.0 0.0 968.0 10.0 16 | C85 c 16.0 22.0 41.0 552.0 744.0 10.0 17 | C87 c 28.0 18.0 26.0 530.0 568.0 10.0 18 | C46 c 2.0 48.0 1.0 46.0 182.0 10.0 19 | C61 c 12.0 24.0 13.0 329.0 551.0 10.0 20 | C48 c 13.0 52.0 36.0 418.0 558.0 10.0 21 | C42 c 24.0 12.0 5.0 0.0 964.0 10.0 22 | C72 c 47.0 16.0 25.0 104.0 252.0 10.0 23 | C11 c 20.0 65.0 12.0 0.0 956.0 10.0 24 | 25 | Q Vehicle fuel tank capacity /60.63/ 26 | C Vehicle load capacity /1000.0/ 27 | r fuel consumption rate /1.0/ 28 | g inverse refueling rate /0.49/ 29 | v average Velocity /1.0/ 30 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/r105C15.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 35.0 35.0 0.0 0.0 230.0 0.0 3 | S0 f 35.0 35.0 0.0 0.0 230.0 0.0 4 | S1 f 64.0 37.0 0.0 0.0 230.0 0.0 5 | S3 f 48.0 60.0 0.0 0.0 230.0 0.0 6 | S13 f 21.0 22.0 0.0 0.0 230.0 0.0 7 | S17 f 51.0 7.0 0.0 0.0 230.0 0.0 8 | S19 f 64.0 19.0 0.0 0.0 230.0 0.0 9 | C23 c 55.0 5.0 29.0 87.0 117.0 10.0 10 | C57 c 32.0 12.0 7.0 166.0 196.0 10.0 11 | C50 c 47.0 47.0 13.0 108.0 138.0 10.0 12 | C69 c 37.0 47.0 6.0 141.0 171.0 10.0 13 | C85 c 16.0 22.0 41.0 83.0 113.0 10.0 14 | C25 c 65.0 20.0 6.0 156.0 186.0 10.0 15 | C56 c 53.0 12.0 6.0 48.0 78.0 10.0 16 | C5 c 15.0 30.0 26.0 153.0 183.0 10.0 17 | C70 c 37.0 56.0 5.0 39.0 69.0 10.0 18 | C28 c 41.0 37.0 16.0 183.0 213.0 10.0 19 | C29 c 64.0 42.0 9.0 107.0 137.0 10.0 20 | C8 c 10.0 43.0 9.0 27.0 57.0 10.0 21 | C9 c 55.0 60.0 16.0 72.0 102.0 10.0 22 | C95 c 25.0 24.0 20.0 130.0 160.0 10.0 23 | C17 c 5.0 30.0 2.0 77.0 107.0 10.0 24 | 25 | Q Vehicle fuel tank capacity /60.63/ 26 | C Vehicle load capacity /200.0/ 27 | r fuel consumption rate /1.0/ 28 | g inverse refueling rate /0.49/ 29 | v average Velocity /1.0/ 30 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/r202C15.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 35.0 35.0 0.0 0.0 1000.0 0.0 3 | S0 f 35.0 35.0 0.0 0.0 1000.0 0.0 4 | S5 f 28.0 62.0 0.0 0.0 1000.0 0.0 5 | S9 f 15.0 42.0 0.0 0.0 1000.0 0.0 6 | S13 f 21.0 22.0 0.0 0.0 1000.0 0.0 7 | S15 f 34.0 16.0 0.0 0.0 1000.0 0.0 8 | S19 f 64.0 19.0 0.0 0.0 1000.0 0.0 9 | C79 c 57.0 48.0 23.0 0.0 964.0 10.0 10 | C44 c 11.0 14.0 18.0 42.0 178.0 10.0 11 | C23 c 55.0 5.0 29.0 53.0 233.0 10.0 12 | C38 c 5.0 5.0 16.0 0.0 947.0 10.0 13 | C25 c 65.0 20.0 6.0 418.0 532.0 10.0 14 | C41 c 42.0 7.0 5.0 782.0 912.0 10.0 15 | C70 c 37.0 56.0 5.0 0.0 968.0 10.0 16 | C85 c 16.0 22.0 41.0 552.0 744.0 10.0 17 | C87 c 28.0 18.0 26.0 530.0 568.0 10.0 18 | C46 c 2.0 48.0 1.0 46.0 182.0 10.0 19 | C61 c 12.0 24.0 13.0 329.0 551.0 10.0 20 | C48 c 13.0 52.0 36.0 418.0 558.0 10.0 21 | C42 c 24.0 12.0 5.0 0.0 964.0 10.0 22 | C72 c 47.0 16.0 25.0 104.0 252.0 10.0 23 | C11 c 20.0 65.0 12.0 0.0 956.0 10.0 24 | 25 | Q Vehicle fuel tank capacity /60.63/ 26 | C Vehicle load capacity /1000.0/ 27 | r fuel consumption rate /1.0/ 28 | g inverse refueling rate /0.49/ 29 | v average Velocity /1.0/ 30 | -------------------------------------------------------------------------------- /EVRPTW/ACO/utils/plot/plot_fitness.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | 5 | def plot_fitness_improvement_per_run(directory): 6 | """ 7 | Plots fitness improvement over iterations for each run in the CSV files in the given directory. 8 | Saves the plots in a directory named 'plot_fitness' within the same location as the CSV file. 9 | """ 10 | for root, dirs, files in os.walk(directory): 11 | for file in files: 12 | if file.endswith('fitness_result.csv'): 13 | file_path = os.path.join(root, file) 14 | df = pd.read_csv(file_path) 15 | 16 | with open(file_path, 'r') as f: 17 | last_line = f.readlines()[-1] 18 | average_last_iterations = float(last_line.split(',')[-1]) 19 | 20 | df = df.iloc[:-1] 21 | 22 | # Create folder for save graphs 23 | graphs_directory = os.path.join(root, "plot_fitness") 24 | if not os.path.exists(graphs_directory): 25 | os.makedirs(graphs_directory) 26 | 27 | # Name folder fot graphs titile 28 | graph_title = os.path.basename(root) 29 | 30 | for run, run_df in df.groupby('run'): 31 | if not run_df.empty: 32 | fig, ax = plt.subplots(figsize=(10, 6)) 33 | ax.plot(run_df['improvement_iteration'], run_df['fitness_improvement'], marker='o') 34 | ax.set_title(f'Fitness Improvement - {graph_title} - Run {run}') 35 | ax.set_xlabel('Improvement Iteration') 36 | ax.set_ylabel('Fitness Improvement') 37 | ax.grid(True) 38 | 39 | fig.subplots_adjust(bottom=0.2) 40 | fig.text(0.5, 0.02, f'Average Fitness of Last Iterations: {average_last_iterations:.2f}', 41 | ha="center", fontsize=10, bbox={"facecolor":"lightgray", "alpha":0.7, "pad":5}) 42 | 43 | plt.savefig(os.path.join(graphs_directory, f'{file[:-4]}_Run_{run}.png')) 44 | plt.close(fig) 45 | 46 | if __name__ == "__main__": 47 | results_directory = '../ACO/results' 48 | plot_fitness_improvement_per_run(results_directory) 49 | 50 | -------------------------------------------------------------------------------- /EVRPTW/ACO/test_instances/rc204C15.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 960.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 960.0 0.0 4 | S7 f 21.0 83.0 0.0 0.0 960.0 0.0 5 | S9 f 14.0 59.0 0.0 0.0 960.0 0.0 6 | S11 f 10.0 28.0 0.0 0.0 960.0 0.0 7 | S13 f 22.0 34.0 0.0 0.0 960.0 0.0 8 | S14 f 27.0 10.0 0.0 0.0 960.0 0.0 9 | S17 f 61.0 14.0 0.0 0.0 960.0 0.0 10 | C79 c 6.0 68.0 30.0 39.0 159.0 10.0 11 | C10 c 10.0 40.0 30.0 0.0 918.0 10.0 12 | C61 c 45.0 65.0 9.0 618.0 738.0 10.0 13 | C2 c 22.0 75.0 30.0 0.0 919.0 10.0 14 | C98 c 26.0 52.0 9.0 0.0 935.0 10.0 15 | C49 c 42.0 12.0 10.0 0.0 911.0 10.0 16 | C1 c 25.0 85.0 20.0 300.0 420.0 10.0 17 | C63 c 65.0 20.0 6.0 0.0 910.0 10.0 18 | C74 c 20.0 20.0 8.0 0.0 913.0 10.0 19 | C75 c 5.0 5.0 16.0 0.0 879.0 10.0 20 | C22 c 40.0 15.0 40.0 0.0 915.0 10.0 21 | C20 c 42.0 15.0 10.0 208.0 328.0 10.0 22 | C76 c 60.0 12.0 31.0 0.0 907.0 10.0 23 | C48 c 42.0 5.0 10.0 0.0 904.0 10.0 24 | C86 c 21.0 24.0 28.0 0.0 917.0 10.0 25 | 26 | Q Vehicle fuel tank capacity /77.75/ 27 | C Vehicle load capacity /1000.0/ 28 | r fuel consumption rate /1.0/ 29 | g inverse refueling rate /0.39/ 30 | v average Velocity /1.0/ 31 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/rc204C15.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 40.0 50.0 0.0 0.0 960.0 0.0 3 | S0 f 40.0 50.0 0.0 0.0 960.0 0.0 4 | S7 f 21.0 83.0 0.0 0.0 960.0 0.0 5 | S9 f 14.0 59.0 0.0 0.0 960.0 0.0 6 | S11 f 10.0 28.0 0.0 0.0 960.0 0.0 7 | S13 f 22.0 34.0 0.0 0.0 960.0 0.0 8 | S14 f 27.0 10.0 0.0 0.0 960.0 0.0 9 | S17 f 61.0 14.0 0.0 0.0 960.0 0.0 10 | C79 c 6.0 68.0 30.0 39.0 159.0 10.0 11 | C10 c 10.0 40.0 30.0 0.0 918.0 10.0 12 | C61 c 45.0 65.0 9.0 618.0 738.0 10.0 13 | C2 c 22.0 75.0 30.0 0.0 919.0 10.0 14 | C98 c 26.0 52.0 9.0 0.0 935.0 10.0 15 | C49 c 42.0 12.0 10.0 0.0 911.0 10.0 16 | C1 c 25.0 85.0 20.0 300.0 420.0 10.0 17 | C63 c 65.0 20.0 6.0 0.0 910.0 10.0 18 | C74 c 20.0 20.0 8.0 0.0 913.0 10.0 19 | C75 c 5.0 5.0 16.0 0.0 879.0 10.0 20 | C22 c 40.0 15.0 40.0 0.0 915.0 10.0 21 | C20 c 42.0 15.0 10.0 208.0 328.0 10.0 22 | C76 c 60.0 12.0 31.0 0.0 907.0 10.0 23 | C48 c 42.0 5.0 10.0 0.0 904.0 10.0 24 | C86 c 21.0 24.0 28.0 0.0 917.0 10.0 25 | 26 | Q Vehicle fuel tank capacity /77.75/ 27 | C Vehicle load capacity /1000.0/ 28 | r fuel consumption rate /1.0/ 29 | g inverse refueling rate /0.39/ 30 | v average Velocity /1.0/ 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /EVRPTW/ACO/test_instances/r102C15.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 35.0 35.0 0.0 0.0 230.0 0.0 3 | S0 f 35.0 35.0 0.0 0.0 230.0 0.0 4 | S1 f 64.0 37.0 0.0 0.0 230.0 0.0 5 | S5 f 28.0 62.0 0.0 0.0 230.0 0.0 6 | S7 f 20.0 61.0 0.0 0.0 230.0 0.0 7 | S9 f 15.0 42.0 0.0 0.0 230.0 0.0 8 | S11 f 11.0 18.0 0.0 0.0 230.0 0.0 9 | S12 f -2.0 25.0 0.0 0.0 230.0 0.0 10 | S14 f 25.0 4.0 0.0 0.0 230.0 0.0 11 | C38 c 5.0 5.0 16.0 113.0 123.0 10.0 12 | C11 c 20.0 65.0 12.0 55.0 65.0 10.0 13 | C45 c 6.0 38.0 16.0 125.0 135.0 10.0 14 | C2 c 35.0 17.0 7.0 0.0 202.0 10.0 15 | C7 c 20.0 50.0 5.0 88.0 98.0 10.0 16 | C59 c 21.0 24.0 28.0 101.0 111.0 10.0 17 | C75 c 49.0 11.0 18.0 0.0 192.0 10.0 18 | C48 c 13.0 52.0 36.0 147.0 157.0 10.0 19 | C17 c 5.0 30.0 2.0 87.0 97.0 10.0 20 | C1 c 41.0 49.0 10.0 36.0 46.0 10.0 21 | C70 c 37.0 56.0 5.0 49.0 59.0 10.0 22 | C91 c 15.0 19.0 1.0 87.0 97.0 10.0 23 | C52 c 27.0 43.0 9.0 111.0 121.0 10.0 24 | C95 c 25.0 24.0 20.0 140.0 150.0 10.0 25 | C80 c 56.0 37.0 6.0 108.0 118.0 10.0 26 | 27 | Q Vehicle fuel tank capacity /60.63/ 28 | C Vehicle load capacity /200.0/ 29 | r fuel consumption rate /1.0/ 30 | g inverse refueling rate /0.49/ 31 | v average Velocity /1.0/ 32 | -------------------------------------------------------------------------------- /EVRPTW/ACO/paper_total_instances/r102C15.txt: -------------------------------------------------------------------------------- 1 | StringID Type x y demand ReadyTime DueDate ServiceTime 2 | D0 d 35.0 35.0 0.0 0.0 230.0 0.0 3 | S0 f 35.0 35.0 0.0 0.0 230.0 0.0 4 | S1 f 64.0 37.0 0.0 0.0 230.0 0.0 5 | S5 f 28.0 62.0 0.0 0.0 230.0 0.0 6 | S7 f 20.0 61.0 0.0 0.0 230.0 0.0 7 | S9 f 15.0 42.0 0.0 0.0 230.0 0.0 8 | S11 f 11.0 18.0 0.0 0.0 230.0 0.0 9 | S12 f -2.0 25.0 0.0 0.0 230.0 0.0 10 | S14 f 25.0 4.0 0.0 0.0 230.0 0.0 11 | C38 c 5.0 5.0 16.0 113.0 123.0 10.0 12 | C11 c 20.0 65.0 12.0 55.0 65.0 10.0 13 | C45 c 6.0 38.0 16.0 125.0 135.0 10.0 14 | C2 c 35.0 17.0 7.0 0.0 202.0 10.0 15 | C7 c 20.0 50.0 5.0 88.0 98.0 10.0 16 | C59 c 21.0 24.0 28.0 101.0 111.0 10.0 17 | C75 c 49.0 11.0 18.0 0.0 192.0 10.0 18 | C48 c 13.0 52.0 36.0 147.0 157.0 10.0 19 | C17 c 5.0 30.0 2.0 87.0 97.0 10.0 20 | C1 c 41.0 49.0 10.0 36.0 46.0 10.0 21 | C70 c 37.0 56.0 5.0 49.0 59.0 10.0 22 | C91 c 15.0 19.0 1.0 87.0 97.0 10.0 23 | C52 c 27.0 43.0 9.0 111.0 121.0 10.0 24 | C95 c 25.0 24.0 20.0 140.0 150.0 10.0 25 | C80 c 56.0 37.0 6.0 108.0 118.0 10.0 26 | 27 | Q Vehicle fuel tank capacity /60.63/ 28 | C Vehicle load capacity /200.0/ 29 | r fuel consumption rate /1.0/ 30 | g inverse refueling rate /0.49/ 31 | v average Velocity /1.0/ 32 | -------------------------------------------------------------------------------- /EVRPTW/docs/target.md: -------------------------------------------------------------------------------- 1 | # **Module: Target** 2 | 3 | This document describes the `Node` class, which represents a node in an Electric Vehicle Routing Problem with Time Windows (EVRPTW). 4 | 5 | ## Class: Node 6 | 7 | ### Attributes 8 | 9 | - `idx` (int): Unique index or identifier for the node. 10 | - `string_id` (str): String ID to represent the node. Example formats include 'D0' for depot, 'S0' for station, 'C20' for customer. 11 | - `node_type` (str): Type of the node, which can be 'd' for depot, 'f' for station, or 'c' for customer. 12 | - `x` (float): The x-coordinate of the node's location on a map or grid. 13 | - `y` (float): The y-coordinate of the node's location on a map or grid. 14 | - `demand` (float): The demand or requirement at the node, relevant mainly for customers. 15 | - `ready_time` (float): The earliest time at which service can begin at this node, used in time windows. 16 | - `due_date` (float): The latest time by which service should be completed at this node, used in time windows. 17 | - `service_time` (float): The time required to service the node, relevant for scheduling and routing. 18 | 19 | ### Constructor 20 | 21 | ```python 22 | def __init__(self, idx, string_id, node_type, x, y, demand, ready_time, due_date, service_time): 23 | # Initializes a Node instance for EVRPTW 24 | ``` 25 | 26 | #### Parameters 27 | 28 | - `idx` (int): Unique index or identifier for the node. 29 | - `string_id` (str): String representation ID for the node. 30 | - `node_type` (str): Character indicating the type of node ('d', 'f', 'c'). 31 | - `x` (float): The x-coordinate of the node's location. 32 | - `y` (float): The y-coordinate of the node's location. 33 | - `demand` (float): Demand or requirement at the node (relevant for customers). 34 | - `ready_time` (float): Earliest time service can begin at the node. 35 | - `due_date` (float): Latest time by which service should be completed at the node. 36 | - `service_time` (float): Time required to service the node. 37 | 38 | ## Method Descriptions 39 | 40 | ### Customer Check 41 | 42 | #### `is_customer(self)` 43 | 44 | - **Purpose:** Checks if the node represents a customer. 45 | - **Returns:** `bool` - Returns `True` if the node is a customer, otherwise `False`. 46 | 47 | ### Depot Check 48 | 49 | #### `is_depot(self)` 50 | 51 | - **Purpose:** Identifies if the node is the depot. 52 | - **Returns:** `bool` - Returns `True` for a depot node, `False` for others. 53 | 54 | ### Charging Station Check 55 | 56 | #### `is_station(self)` 57 | 58 | - **Purpose:** Determines if the node is a charging station. 59 | - **Returns:** `bool` - True for a charging station, False otherwise. -------------------------------------------------------------------------------- /EVRPTW/docs/main.md: -------------------------------------------------------------------------------- 1 | # Ant Colony System Simulation 2 | 3 | This script implements a simulation for solving the Electric Vehicle Routing Problem with Time Windows (EVRPTW) using a Multiple Ant Colony System (MACS). 4 | 5 | ## Dependencies 6 | 7 | - `numpy` 8 | - `dask` 9 | - `pandas` 10 | - `yaml` 11 | - `csv` 12 | - `os` 13 | - `time` 14 | 15 | ## Classes 16 | 17 | - `AntColonySystem`: From the `acsd` module, simulates an ant colony system. 18 | - `EvrptwGraph`: From the `evrptw_config` module, represents the EVRPTW graph. 19 | 20 | ## Functions 21 | 22 | ### `load_configs(general_config_file, aco_config_file)` 23 | 24 | Loads configuration settings from YAML files. 25 | 26 | - **Parameters:** 27 | - `general_config_file`: Path to the general configuration file. 28 | - `aco_config_file`: Path to the ACO-specific configuration file. 29 | - **Returns:** 30 | - `dict`: Configuration data. 31 | 32 | ### `run_macs(run, file_path, max_iter, ants_num, alpha, beta, q0, k1)` 33 | 34 | Runs a single instance of the MACS simulation. 35 | 36 | - **Parameters:** 37 | - `run`: Run number. 38 | - `file_path`: Path to the data file. 39 | - `max_iter`: Maximum number of iterations. 40 | - `ants_num`: Number of ants. 41 | - `alpha`: Alpha parameter for MACS. 42 | - `beta`: Beta parameter for MACS. 43 | - `q0`: Q0 parameter for MACS. 44 | - `k1`: K1 parameter for MACS. 45 | - **Returns:** 46 | - `tuple`: Improvements and path details. 47 | 48 | ### `save_results_to_csv(results, filename)` 49 | 50 | Saves results to a CSV file using Dask. 51 | 52 | - **Parameters:** 53 | - `results`: Results data to save. 54 | - `filename`: Output file name. 55 | 56 | ### `save_path_details_to_csv(path_details, filename)` 57 | 58 | Saves path details to a CSV file using Dask. 59 | 60 | - **Parameters:** 61 | - `path_details`: Path details data to save. 62 | - `filename`: Output file name. 63 | 64 | ### `calculate_and_append_average(filename)` 65 | 66 | Calculates and appends the average of the last improvements to the CSV file. 67 | 68 | - **Parameters:** 69 | - `filename`: The CSV file to update. 70 | 71 | ## Main Execution 72 | 73 | - Configures and initializes the Dask client. 74 | - Processes each `.txt` file in the specified directory for EVRPTW instances. 75 | - Executes multiple runs of MACS for each instance. 76 | - Saves results and path details to CSV files. 77 | - Calculates and appends the average fitness of the last iterations. 78 | 79 | ## Usage 80 | 81 | Run the script in a Python environment where all dependencies are installed. Ensure that the necessary configuration files and EVRPTW instance files are available in the specified directories. 82 | -------------------------------------------------------------------------------- /EVRPTW/ACO/utils/plot/plot_fitness_penalty.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | 5 | def plot_fitness_penalty_combined(directory): 6 | """ 7 | Plots fitness and penalty over iterations for each run in the CSV files in the given directory, 8 | ensuring that the penalty axis is scaled independently from the fitness values. 9 | """ 10 | for root, dirs, files in os.walk(directory): 11 | for file in files: 12 | if file.endswith('fitness_result.csv'): 13 | file_path = os.path.join(root, file) 14 | df = pd.read_csv(file_path) 15 | 16 | # Remove the last line and check for non-zero penalty values 17 | df = df.iloc[:-1] 18 | if df['penalty'].eq(0).all(): 19 | continue # Skip the file if all penalty values are zero 20 | 21 | # Create a directory for saving the plots 22 | graphs_directory = os.path.join(root, "combined_plots") 23 | if not os.path.exists(graphs_directory): 24 | os.makedirs(graphs_directory) 25 | 26 | graph_title = os.path.basename(root) 27 | 28 | for run, run_df in df.groupby('run'): 29 | if not run_df.empty: 30 | fig, ax1 = plt.subplots(figsize=(10, 6)) 31 | 32 | # Plot fitness on the primary y-axis 33 | ax1.set_xlabel('Improvement Iteration') 34 | ax1.set_ylabel('Fitness', color='tab:blue') 35 | ax1.plot(run_df['improvement_iteration'], run_df['fitness_improvement'], 36 | 'o-', color='tab:blue', markersize=5, linewidth=2) 37 | ax1.tick_params(axis='y', labelcolor='tab:blue') 38 | 39 | # Set up the secondary y-axis for penalty with an independent scale 40 | ax2 = ax1.twinx() 41 | ax2.set_ylabel('Penalty', color='tab:red') 42 | # Scale the penalty axis independently 43 | ax2.set_ylim([0, max(run_df['penalty']) * 2.5]) # Give some headroom for the penalty values 44 | ax2.plot(run_df['improvement_iteration'], run_df['penalty'], 45 | 'o-', color='tab:red', markersize=5, linewidth=2) 46 | ax2.tick_params(axis='y', labelcolor='tab:red') 47 | 48 | plt.title(f'Fitness and Penalty Improvement - {graph_title} - Run {run}') 49 | plt.grid(True) 50 | 51 | plt.savefig(os.path.join(graphs_directory, f'{file[:-4]}_Run_{run}_Combined.png')) 52 | plt.close(fig) 53 | 54 | 55 | if __name__ == "__main__": 56 | results_directory = '../../results/' 57 | plot_fitness_penalty_combined(results_directory) -------------------------------------------------------------------------------- /EVRPTW/ACO/utils/percentage_feasible_solution.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pandas as pd 3 | 4 | def calculate_feasibility_percentage(directory): 5 | """ 6 | Calculate the feasibility percentage of solutions across all CSV files in the given directory. 7 | 8 | Args: 9 | - directory (str): The path to the directory containing subdirectories with CSV files. 10 | 11 | Returns: 12 | - list of dicts: A list containing dictionaries with filepath and feasibility percentage. 13 | """ 14 | feasibility_results = [] 15 | 16 | # Iterate through all subdirectories in the given directory 17 | for subdir, _, files in os.walk(directory): 18 | for file in files: 19 | if file.endswith('fitness_result.csv'): 20 | file_path = os.path.join(subdir, file) 21 | 22 | # Load the CSV file into a DataFrame 23 | df = pd.read_csv(file_path) 24 | 25 | # Check if the 'penalty' column exists in the DataFrame 26 | if 'penalty' in df.columns: 27 | # Calculate the percentage of solutions with zero penalty 28 | zero_penalty_count = df['penalty'].apply(lambda x: x == 0).sum() 29 | total_count = len(df) 30 | feasibility_percentage = (zero_penalty_count / total_count) * 100 31 | 32 | # Add the result to the feasibility_results list 33 | feasibility_results.append({ 34 | 'filepath': file_path, 35 | 'feasibility_percentage': feasibility_percentage 36 | }) 37 | 38 | return feasibility_results 39 | 40 | def write_results_to_csv(results, output_filename): 41 | """ 42 | Write the feasibility results to a CSV file and append the overall average feasibility percentage. 43 | 44 | Args: 45 | - results (list of dicts): The feasibility results to write. 46 | - output_filename (str): The name of the output CSV file. 47 | """ 48 | df = pd.DataFrame(results) 49 | 50 | average_feasibility = df['feasibility_percentage'].mean() 51 | 52 | df.to_csv(output_filename, index=False) 53 | 54 | with open(output_filename, 'a') as f: 55 | f.write(f"\nAverage Feasibility Percentage,{average_feasibility}") 56 | 57 | if __name__ == "__main__": 58 | # Existing directory containing the results 59 | results_directory = "../results_k1_k2_no_ls/" 60 | 61 | # New directory for saving the summary file 62 | summary_directory = "../percentage_feasible_solution/" 63 | if not os.path.exists(summary_directory): 64 | os.makedirs(summary_directory) 65 | 66 | # Define the output file name in the new directory 67 | output_filename = os.path.join(summary_directory, "final_feasibility_results.csv") 68 | 69 | # Calculate the feasibility percentages 70 | results = calculate_feasibility_percentage(results_directory) 71 | 72 | # Write the results to the output file in the new directory 73 | write_results_to_csv(results, output_filename) 74 | print(f"Feasibility results written to {output_filename}.") 75 | -------------------------------------------------------------------------------- /EVRPTW/docs/acsd.md: -------------------------------------------------------------------------------- 1 | # Module: Ant Colony System (ACS) 2 | 3 | ## Class: AntColonySystem 4 | 5 | The `AntColonySystem` class implements a Multiple Ant Colony System for solving the Electric Vehicle Routing Problem with Time Windows (EVRPTW). 6 | 7 | ### Constructor 8 | 9 | ```python 10 | def __init__(self, graph: EvrptwGraph, ants_num=10, max_iter=400, alpha=1, beta=2, q0=0.9, k1=2, apply_local_search=True)` 11 | # Initializes the Ant Colony System. 12 | ``` 13 | - **Parameters:** 14 | - `graph` (EvrptwGraph): The graph representing the EVRPTW problem. 15 | - `ants_num` (int, default=10): Number of ants in the system. 16 | - `max_iter` (int, default=400): Maximum number of iterations. 17 | - `alpha` (float, default=1): Parameter influencing the importance of pheromone. 18 | - `beta` (float, default=2): Parameter influencing the importance of heuristic information. 19 | - `q0` (float, default=0.9): Parameter for pseudo-random proportional decision rule. 20 | - `k1` (float, default=2): Coefficient for time window penalty. 21 | - `apply_local_search` (bool, default=True): Flag to apply local search or not. 22 | 23 | ### Methods 24 | 25 | #### `_construct_solution(self, sigma: int)` 26 | 27 | Constructs a solution (path) based on the ant colony system and constraints. 28 | 29 | - **Parameters:** 30 | - `sigma` (int): Number of vehicles. 31 | - **Returns:** 32 | - `tuple`: The constructed travel path and cumulative time window penalty. 33 | 34 | #### `evaluate_solution(self, travel_path: list)` 35 | 36 | Evaluates the total distance of a given travel path. 37 | 38 | - **Parameters:** 39 | - `travel_path` (list): The travel path to evaluate. 40 | - **Returns:** 41 | - `float`: The total distance of the travel path. 42 | 43 | #### `_ACS_DIST_G(self, apply_local_search=True)` 44 | 45 | Performs the Ant Colony System algorithm for EVRPTW. 46 | 47 | - **Parameters:** 48 | - `apply_local_search` (bool): Flag to apply local search or not. 49 | - **Returns:** 50 | - `tuple`: Final best path distance, the best path, and various histories. 51 | 52 | #### `select_next_index(self, ant, nodes)` 53 | 54 | Selects the next node index for the ant to visit. 55 | 56 | - **Parameters:** 57 | - `ant` (Ant): The ant making the decision. 58 | - `nodes` (set): Set of node indices. 59 | - **Returns:** 60 | - `int`: The chosen node index. 61 | 62 | #### `stochastic_accept(index_to_visit, transition_prob)` 63 | 64 | Stochastic acceptance rule for selecting the next index. 65 | 66 | - **Parameters:** 67 | - `index_to_visit` (list): List of indices to visit. 68 | - `transition_prob` (list): Transition probabilities. 69 | - **Returns:** 70 | - `int`: The chosen index. 71 | 72 | #### `roulette_wheel_selection(self, ants: list)` 73 | 74 | Roulette wheel selection to pick an ant. 75 | 76 | - **Parameters:** 77 | - `ants` (list): List of ant instances. 78 | - **Returns:** 79 | - `Ant`: The selected ant. 80 | 81 | #### `get_active_vei(self, path: list)` 82 | 83 | Calculates the number of active vehicles used in a given path. 84 | 85 | - **Parameters:** 86 | - `path` (list): The path containing node indices, including the depot (0). 87 | - **Returns:** 88 | - `int`: The number of active vehicles used in the path. 89 | 90 | -------------------------------------------------------------------------------- /EVRPTW/ACO/target.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | """ 3 | Represents a node in an Electric Vehicle Routing Problem with Time Windows (EVRPTW). 4 | 5 | Attributes: 6 | idx (int): Unique index or identifier for the node. 7 | string_id (str): String ID to represent the node. Example formats include 'D0' for depot, 'S0' for station, 'C20' for customer. 8 | node_type (str): Type of the node, which can be 'd' for depot, 'f' for station, or 'c' for customer. 9 | x (float): The x-coordinate of the node's location on a map or grid. 10 | y (float): The y-coordinate of the node's location on a map or grid. 11 | demand (float): The demand or requirement at the node, relevant mainly for customers. 12 | ready_time (float): The earliest time at which service can begin at this node, used in time windows. 13 | due_date (float): The latest time by which service should be completed at this node, used in time windows. 14 | service_time (float): The time required to service the node, relevant for scheduling and routing. 15 | 16 | Methods: 17 | is_depot: Checks if the node is a depot. 18 | is_station: Checks if the node is a charging station. 19 | is_customer: Checks if the node is a customer. 20 | """ 21 | 22 | def __init__( 23 | self, 24 | idx, 25 | string_id, 26 | node_type, 27 | x, 28 | y, 29 | demand, 30 | ready_time, 31 | due_date, 32 | service_time, 33 | ): 34 | """ 35 | Initializes a Node instance for EVRPTW. 36 | 37 | Parameters: 38 | idx (int): Unique index or identifier for the node. 39 | string_id (str): String representation ID for the node. 40 | node_type (str): Character indicating the type of node ('d', 'f', 'c'). 41 | x (float): The x-coordinate of the node's location. 42 | y (float): The y-coordinate of the node's location. 43 | demand (float): Demand or requirement at the node (relevant for customers). 44 | ready_time (float): Earliest time service can begin at the node. 45 | due_date (float): Latest time by which service should be completed at the node. 46 | service_time (float): Time required to service the node. 47 | """ 48 | self.idx = idx 49 | self.string_id = string_id 50 | self.node_type = node_type 51 | self.x = x 52 | self.y = y 53 | self.demand = demand 54 | self.ready_time = ready_time 55 | self.due_date = due_date 56 | self.service_time = service_time 57 | 58 | def is_depot(self): 59 | """ 60 | Determines if the node is a depot. 61 | 62 | Returns: 63 | bool: True if the node is a depot, False otherwise. 64 | """ 65 | return self.node_type == "d" 66 | 67 | def is_station(self): 68 | """ 69 | Determines if the node is a charging station. 70 | 71 | Returns: 72 | bool: True if the node is a charging station, False otherwise. 73 | """ 74 | return self.node_type == "f" 75 | 76 | def is_customer(self): 77 | """ 78 | Determines if the node is a customer. 79 | 80 | Returns: 81 | bool: True if the node is a customer, False otherwise. 82 | """ 83 | return self.node_type == "c" 84 | -------------------------------------------------------------------------------- /EVRPTW/ACO/utils/plot/gif_and_path_plot.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | import ast 5 | import imageio.v2 6 | 7 | def create_gif_for_path_evolution(directory, gif_name='path_evolution.gif', fps=1): 8 | for root, dirs, files in os.walk(directory): 9 | for file in files: 10 | if file == 'path_results.csv': 11 | file_path = os.path.join(root, file) 12 | df = pd.read_csv(file_path) 13 | 14 | graphs_directory = os.path.join(root, "plot_paths") 15 | if not os.path.exists(graphs_directory): 16 | os.makedirs(graphs_directory) 17 | 18 | # Extract instance name from the directory or file path 19 | instance_name = os.path.basename(root) 20 | 21 | images = [] 22 | 23 | for (run, num_improvement), group_df in df.groupby(['run', 'numer_of_improvement']): 24 | fig, ax = plt.subplots(figsize=(15, 10)) 25 | 26 | coordinates_list = [] 27 | node_types_list = [] 28 | 29 | for index, row in group_df.iterrows(): 30 | coordinates_str = "[" + row['coordinates'].replace(");(", "),(") + "]" 31 | coordinates = ast.literal_eval(coordinates_str) 32 | node_types = ast.literal_eval(row['node_types']) 33 | 34 | coordinates_list.extend(coordinates) 35 | node_types_list.extend(node_types) 36 | 37 | for coord, node_type in zip(coordinates, node_types): 38 | color = 'lightgrey' 39 | if node_type == 'd': 40 | color = 'blue' 41 | elif node_type == 'f': 42 | color = 'green' 43 | elif node_type == 'c': 44 | color = 'red' 45 | 46 | ax.scatter(*coord, color=color, s=70) 47 | 48 | if coordinates_list: 49 | x_coords, y_coords = zip(*coordinates_list) 50 | ax.plot(x_coords, y_coords, linestyle='--', color='lightgrey', linewidth=2, alpha=0.6) 51 | 52 | # Include the instance name in the title 53 | ax.set_title(f'{instance_name} - Run {run} - Improvement {num_improvement}') 54 | ax.set_xlabel('Coordinate X') 55 | ax.set_ylabel('Coordinate Y') 56 | ax.grid(False) 57 | 58 | # Add legend 59 | legend_elements = [ 60 | plt.Line2D([0], [0], marker='o', color='w', label='Depot (d)', markerfacecolor='blue', markersize=10), 61 | plt.Line2D([0], [0], marker='o', color='w', label='Fuel Station (f)', markerfacecolor='green', markersize=10), 62 | plt.Line2D([0], [0], marker='o', color='w', label='Customer (c)', markerfacecolor='red', markersize=10) 63 | ] 64 | 65 | ax.legend(handles=legend_elements) 66 | 67 | img_path = os.path.join(graphs_directory, f'Run_{run}_Improvement_{num_improvement}.png') 68 | plt.savefig(img_path) 69 | plt.close(fig) 70 | 71 | images.append(imageio.imread(img_path)) 72 | 73 | gif_path = os.path.join(graphs_directory, gif_name) 74 | imageio.mimsave(gif_path, images, fps=fps) 75 | 76 | print(f"GIF created at {gif_path}") 77 | 78 | if __name__ == "__main__": 79 | results_directory = '../../results/' 80 | create_gif_for_path_evolution(results_directory, gif_name='path_evolution.gif', fps=1) 81 | -------------------------------------------------------------------------------- /EVRPTW/docs/evrptw_config.md: -------------------------------------------------------------------------------- 1 | # **Module: Evrptw Config** 2 | 3 | This module contains the `EvrptwGraph` class, which is designed to solve the Electric Vehicle Routing Problem with Time Windows (EVRPTW). 4 | 5 | ## Class: EvrptwGraph 6 | 7 | ### Attributes 8 | 9 | - `node_num` (int): Total number of nodes within the graph. 10 | - `nodes` (list of Node): List of `Node` instances representing the nodes within the EVRPTW graph. 11 | - `node_dist_mat` (numpy.ndarray): Matrix representing the distances between nodes. 12 | - `fuel_stations` (list of Node): List of nodes that serve as fuel stations. 13 | - `depot` (Node): Node that is designated as the depot. 14 | - `tank_capacity` (float): Maximum fuel capacity of the electric vehicle. 15 | - `load_capacity` (float): Maximum carrying capacity of the electric vehicle. 16 | - `fuel_consumption_rate` (float): Rate of fuel consumption by the electric vehicle. 17 | - `charging_rate` (float): Rate at which the vehicle's battery can be charged. 18 | - `velocity` (float): Average velocity of the electric vehicle. 19 | 20 | ### Constructor 21 | 22 | ```python 23 | def __init__(self, file_path, rho=0.1): 24 | # Initializes an EvrptwGraph instance. 25 | ``` 26 | 27 | #### Parameters 28 | 29 | - `file_path` (str): Path to the file containing the EVRPTW instance data. 30 | - `rho` (float, optional): Evaporation rate for the pheromone trail. 31 | 32 | ## Methods Overview 33 | 34 | The `EvrptwGraph` class includes several methods to solve the Electric Vehicle Routing Problem with Time Windows (EVRPTW). These methods are designed to calculate distances, update pheromone levels, select paths and charging stations, and read problem instances. 35 | 36 | ### Global Pheromone Update 37 | 38 | #### `global_update_pheromone(self, best_path: list, best_path_distance: float)` 39 | 40 | - **Purpose:** Updates pheromone levels globally based on the best path found. 41 | - **Returns:** `None` 42 | 43 | ### Distance Matrix Calculation 44 | 45 | #### `calculate_distance_matrix(nodes: list)` 46 | 47 | - **Purpose:** Computes the Euclidean distance matrix for the provided nodes. 48 | - **Returns:** `np.ndarray` representing the distance matrix. 49 | 50 | ### Instance Reading 51 | 52 | #### `_read_instance(self, file_path: str)` 53 | 54 | - **Purpose:** Reads and parses the EVRPTW instance data from a file. 55 | - **Returns:** `tuple` containing nodes, distance matrix, fuel stations, depot, capacities, rates, and velocity. 56 | 57 | ### Closest Charging Station Selection 58 | 59 | #### `select_closest_station(self, i: int, j: int)` 60 | 61 | - **Purpose:** Identifies the nearest charging station to the given nodes. 62 | - **Returns:** `tuple` with the index of the selected station and the distance. Returns (-1, None) if no station is found. 63 | 64 | ### Path Coordinates Mapping 65 | 66 | #### `get_coordinates_from_path(self, path: list)` 67 | 68 | - **Purpose:** Retrieves the (x, y) coordinates for each node in the path. 69 | - **Returns:** List of coordinate tuples for the nodes in the path. 70 | 71 | ### Node Type Mapping 72 | 73 | #### `create_node_type_map(self)` 74 | 75 | - **Purpose:** Generates a map linking each node's index to its type. 76 | - **Returns:** `dict` mapping node indices (int) to their types (str). 77 | 78 | ### Nearest Neighbor Heuristic 79 | 80 | #### `nearest_neighbor_heuristic(self)` 81 | 82 | - **Purpose:** Implements the nearest neighbor heuristic for route planning, considering various constraints. 83 | - **Returns:** `tuple` with the travel path, total travel distance, and the number of vehicles used. 84 | 85 | ### Next Index Calculation 86 | 87 | #### `_cal_nearest_next_index(self, index_to_visit: list, current_index: int, current_battery: float, current_time: float)` 88 | 89 | - **Purpose:** Finds the nearest viable next node for the vehicle to visit. 90 | - **Returns:** `int` index of the nearest next node, or None if none are suitable. 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /EVRPTW/tests/test_acsd.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import numpy as np 4 | 5 | # Set up the script directory and the absolute file path for test instances 6 | script_dir = os.path.dirname(__file__) 7 | file_path = os.path.join(script_dir, '..', 'ACO', 'paper_total_instances', 'c103C5.txt') 8 | file_path = os.path.abspath(file_path) 9 | 10 | # Add the ACO directory to sys.path to ensure imports work 11 | aco_dir = os.path.join(script_dir, '..', 'ACO') 12 | sys.path.insert(0, aco_dir) 13 | 14 | 15 | from target import Node 16 | from evrptw_config import EvrptwGraph 17 | from ant import Ant 18 | import pytest 19 | from acsd import AntColonySystem 20 | 21 | 22 | # Fixture for creating an example graph for testing 23 | @pytest.fixture 24 | def example_graph(): 25 | # Setting up nodes for the graph (depot, customer, and charging station) 26 | nodes = [ 27 | Node(0, "D0", "d", 0, 0, 0, 0, 0, 0), # Depot 28 | Node(1, "C1", "c", 1, 1, 5, 8, 10, 1), # Customer with time window and demand 29 | Node(2, "S1", "f", 2, 2, 0, 0, 0, 0) # Charging station 30 | ] 31 | # Creating the graph and setting its properties 32 | graph = EvrptwGraph(file_path) 33 | graph.nodes = nodes 34 | graph.node_num = len(nodes) 35 | graph.node_dist_mat = np.array([[0, 1.5, 3], [1.5, 0, 1.5], [3, 1.5, 0]]) 36 | graph.tank_capacity = 10 37 | graph.load_capacity = 50 38 | graph.velocity = 1 39 | graph.fuel_consumption_rate = 1 40 | graph.charging_rate = 5 41 | graph.nodes[0].due_date = 100 # Large due date for depot 42 | return graph 43 | 44 | # Test for constructing a solution path 45 | def test_construct_solution(example_graph): 46 | macs_system = AntColonySystem(example_graph) 47 | path, penalty = macs_system._construct_solution(3) 48 | # Verifying that the path starts and ends at the depot 49 | assert path[0] == path[-1] == example_graph.nodes[0].idx 50 | # Verifying that all visited nodes are part of the graph 51 | visited_nodes = set(path) 52 | all_nodes = set(range(len(example_graph.nodes))) 53 | assert visited_nodes.issubset(all_nodes) 54 | 55 | # Test for evaluating a solution with an empty path 56 | def test_evaluate_solution_empty(example_graph): 57 | macs_system = AntColonySystem(example_graph) 58 | # Expecting the distance for an empty path to be 0 59 | assert macs_system.evaluate_solution([]) == 0 60 | 61 | # Test for evaluating a non-empty solution path 62 | def test_evaluate_solution_non_empty(example_graph): 63 | macs_system = AntColonySystem(example_graph) 64 | path = [0, 1, 2, 0] 65 | # Expected distance for the given path 66 | expected_distance = 6.0 67 | assert macs_system.evaluate_solution(path) == expected_distance 68 | 69 | # Test for the ACS algorithm (_ACS_DIST_G) 70 | def test_acs_dist_g(example_graph): 71 | macs_system = AntColonySystem(example_graph) 72 | # Running the ACS algorithm and verifying that it finds a finite solution 73 | C_final, *_ = macs_system._ACS_DIST_G() 74 | assert C_final != float('inf') 75 | 76 | # Test for the roulette wheel selection mechanism 77 | def test_roulette_wheel_selection(example_graph): 78 | macs_system = AntColonySystem(example_graph) 79 | # Creating Ant objects with valid travel paths 80 | ants = [Ant(example_graph) for _ in range(4)] 81 | # Setting a sample travel path for each ant 82 | for ant in ants: 83 | ant.travel_path = [0, 1, 2, 0] 84 | # Performing roulette wheel selection and verifying the selected ant is from the list 85 | selected_ant = macs_system.roulette_wheel_selection(ants) 86 | assert selected_ant in ants 87 | 88 | # Test for calculating the number of active vehicles in a path 89 | def test_get_active_vei(example_graph): 90 | macs_system = AntColonySystem(example_graph) 91 | # Example path using two vehicles 92 | path = [0, 1, 2, 0, 3, 0] 93 | num_vehicles = macs_system.get_active_vei(path) 94 | # Verifying that two vehicles are used in the path 95 | assert num_vehicles == 2 -------------------------------------------------------------------------------- /EVRPTW/ACO/tuning/tuning_k1.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import pandas as pd 4 | from dask.distributed import Client, as_completed 5 | import sys 6 | # Add the directory containing evrptw_config and macs to the system path 7 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 8 | from evrptw_config import EvrptwGraph 9 | from acsd import AntColonySystem 10 | import ast 11 | 12 | def run_macs_for_k(file_path, k1_combinations): 13 | results = [] 14 | for k1 in k1_combinations: 15 | graph = EvrptwGraph(file_path) 16 | macs = AntColonySystem(graph, ants_num=10, max_iter=3, alpha=1, beta=2, q0=0.9, k1=k1) 17 | final_cost, _, _, penalty, _, _, _ = macs._ACS_DIST_G() 18 | 19 | has_zero_penalty = all(p == 0 for p in penalty) 20 | 21 | results.append({ 22 | 'file': os.path.basename(file_path), 23 | 'k1': k1, 24 | 'cost': final_cost, 25 | 'penalty': penalty, 26 | 'has_zero_penalty': has_zero_penalty 27 | }) 28 | return results 29 | 30 | 31 | def find_best_k1(results_df): 32 | zero_penalty_df = results_df[results_df['has_zero_penalty']] 33 | if not zero_penalty_df.empty: 34 | sorted_df = zero_penalty_df.sort_values(by='cost') 35 | else: 36 | sorted_df = results_df.sort_values(by=['penalty_sum', 'cost']) 37 | 38 | best_k1 = sorted_df.head(1) 39 | return best_k1[['k1']] 40 | 41 | 42 | def print_and_save_best_combinations_for_k(csv_file, num_top_combinations=10, output_file='result_grid_search_best_for_k.csv'): 43 | df = pd.read_csv(csv_file) 44 | 45 | df['penalty'] = df['penalty'].apply(lambda x: [float(i) for i in ast.literal_eval(x)]) 46 | df['penalty_sum'] = df['penalty'].apply(sum) 47 | df['has_zero_penalty'] = df['penalty'].apply(lambda x: all(p == 0 for p in x)) 48 | 49 | top_combinations_list = [] 50 | 51 | for _, group_df in df.groupby('file'): 52 | zero_penalty_df = group_df[group_df['has_zero_penalty']] 53 | if not zero_penalty_df.empty: 54 | 55 | top_zero_penalty_combinations = zero_penalty_df.sort_values(by='cost').head(num_top_combinations) 56 | top_combinations_list.append(top_zero_penalty_combinations) 57 | else: 58 | 59 | top_combinations = group_df.sort_values(by=['penalty_sum', 'cost']).head(num_top_combinations) 60 | top_combinations_list.append(top_combinations) 61 | 62 | 63 | top_combinations_df = pd.concat(top_combinations_list) 64 | top_combinations_df.to_csv(output_file, index=False) 65 | 66 | 67 | 68 | def tuning(directory_path): 69 | abs_directory_path = os.path.abspath(directory_path) 70 | k1_values = np.arange(150, 300) 71 | 72 | num_cores = 6 73 | k1_combinations = [k1 for k1 in k1_values] 74 | chunks = [k1_combinations[i::num_cores] for i in range(num_cores)] 75 | client = Client(n_workers=num_cores, threads_per_worker=1) 76 | 77 | futures = [] 78 | for filename in os.listdir(abs_directory_path): 79 | if filename.endswith(".txt"): 80 | file_path = os.path.join(abs_directory_path, filename) 81 | for chunk in chunks: 82 | future = client.submit(run_macs_for_k, file_path, chunk) 83 | futures.append(future) 84 | 85 | results = [] 86 | for future in as_completed(futures): 87 | results.extend(future.result()) 88 | 89 | client.close() 90 | 91 | results_df = pd.DataFrame(results) 92 | 93 | results_df['penalty'] = results_df['penalty'].apply( 94 | lambda x: x if isinstance(x, list) else ast.literal_eval(x) 95 | if isinstance(x, str) else [x] 96 | ) 97 | results_df['penalty_sum'] = results_df['penalty'].apply(sum) 98 | 99 | results_df['has_zero_penalty'] = results_df['penalty'].apply(lambda x: all(p == 0 for p in x)) 100 | 101 | results_df.to_csv('grid_search_results_for_k.csv', index=False) 102 | 103 | best_k1 = find_best_k1(results_df) 104 | best_k1.to_csv('best_value_k1.csv', index=False) 105 | 106 | print_and_save_best_combinations_for_k('grid_search_results_for_k.csv') 107 | 108 | 109 | if __name__ == "__main__": 110 | tuning("../penality_instances") 111 | 112 | -------------------------------------------------------------------------------- /EVRPTW/ACO/tuning/tuning_k1_k2.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import pandas as pd 4 | from dask.distributed import Client, as_completed 5 | import sys 6 | # Add the directory containing evrptw_config and macs to the system path 7 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 8 | from evrptw_config import EvrptwGraph 9 | from acsd import AntColonySystem 10 | import ast 11 | 12 | def run_macs_for_k(file_path, k1_k2_combinations): 13 | results = [] 14 | for k1, k2 in k1_k2_combinations: 15 | graph = EvrptwGraph(file_path) 16 | macs = AntColonySystem(graph, ants_num=10, max_iter=3, alpha=1, beta=2, q0=0.9, k1=k1, k2=k2) 17 | final_cost, _, _, penalty, _, _, _ = macs._ACS_DIST_G() 18 | 19 | has_zero_penalty = all(p == 0 for p in penalty) 20 | 21 | results.append({ 22 | 'file': os.path.basename(file_path), 23 | 'k1': k1, 24 | 'k2': k2, 25 | 'cost': final_cost, 26 | 'penalty': penalty, 27 | 'has_zero_penalty': has_zero_penalty 28 | }) 29 | return results 30 | 31 | 32 | def find_best_k1_k2(results_df): 33 | zero_penalty_df = results_df[results_df['has_zero_penalty']] 34 | if not zero_penalty_df.empty: 35 | sorted_df = zero_penalty_df.sort_values(by='cost') 36 | else: 37 | sorted_df = results_df.sort_values(by=['penalty_sum', 'cost']) 38 | 39 | best_k1_k2 = sorted_df.head(1) 40 | return best_k1_k2[['k1', 'k2']] 41 | 42 | 43 | def print_and_save_best_combinations_for_k(csv_file, num_top_combinations=10, output_file='result_grid_search_best_for_k.csv'): 44 | df = pd.read_csv(csv_file) 45 | 46 | df['penalty'] = df['penalty'].apply(lambda x: [float(i) for i in ast.literal_eval(x)]) 47 | df['penalty_sum'] = df['penalty'].apply(sum) 48 | df['has_zero_penalty'] = df['penalty'].apply(lambda x: all(p == 0 for p in x)) 49 | 50 | top_combinations_list = [] 51 | 52 | for _, group_df in df.groupby('file'): 53 | zero_penalty_df = group_df[group_df['has_zero_penalty']] 54 | if not zero_penalty_df.empty: 55 | 56 | top_zero_penalty_combinations = zero_penalty_df.sort_values(by='cost').head(num_top_combinations) 57 | top_combinations_list.append(top_zero_penalty_combinations) 58 | else: 59 | 60 | top_combinations = group_df.sort_values(by=['penalty_sum', 'cost']).head(num_top_combinations) 61 | top_combinations_list.append(top_combinations) 62 | 63 | 64 | top_combinations_df = pd.concat(top_combinations_list) 65 | top_combinations_df.to_csv(output_file, index=False) 66 | 67 | 68 | 69 | def tuning(directory_path): 70 | abs_directory_path = os.path.abspath(directory_path) 71 | k1_values = np.arange(150, 300) 72 | k2_values = np.arange(1,5) 73 | 74 | num_cores = 6 75 | k1_k2_combinations = [(k1, k2) for k1 in k1_values for k2 in k2_values] 76 | chunks = [k1_k2_combinations[i::num_cores] for i in range(num_cores)] 77 | client = Client(n_workers=num_cores, threads_per_worker=1) 78 | 79 | futures = [] 80 | for filename in os.listdir(abs_directory_path): 81 | if filename.endswith(".txt"): 82 | file_path = os.path.join(abs_directory_path, filename) 83 | for chunk in chunks: 84 | future = client.submit(run_macs_for_k, file_path, chunk) 85 | futures.append(future) 86 | 87 | results = [] 88 | for future in as_completed(futures): 89 | results.extend(future.result()) 90 | 91 | client.close() 92 | 93 | results_df = pd.DataFrame(results) 94 | 95 | results_df['penalty'] = results_df['penalty'].apply( 96 | lambda x: x if isinstance(x, list) else ast.literal_eval(x) 97 | if isinstance(x, str) else [x] 98 | ) 99 | results_df['penalty_sum'] = results_df['penalty'].apply(sum) 100 | 101 | results_df['has_zero_penalty'] = results_df['penalty'].apply(lambda x: all(p == 0 for p in x)) 102 | 103 | results_df.to_csv('grid_search_results_for_k.csv', index=False) 104 | 105 | best_k1_k2 = find_best_k1_k2(results_df) 106 | best_k1_k2.to_csv('best_value_k1_k2.csv', index=False) 107 | 108 | print_and_save_best_combinations_for_k('grid_search_results_for_k.csv') 109 | 110 | 111 | if __name__ == "__main__": 112 | tuning("../penality_instances") 113 | 114 | 115 | -------------------------------------------------------------------------------- /EVRPTW/ACO/tuning/tuning_alpha_beta_q0.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import pandas as pd 4 | from dask.distributed import Client, as_completed 5 | import sys 6 | 7 | # Make sure you adjust the sys.path to include the directory of evrptw_config and macs 8 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 9 | 10 | from evrptw_config import EvrptwGraph 11 | from acsd import AntColonySystem 12 | import ast 13 | 14 | def run_macs_for_alpha_beta_q0_rho(file_path, param_combinations): 15 | results = [] 16 | for alpha, beta, q0, rho in param_combinations: 17 | graph = EvrptwGraph(file_path, rho=rho) 18 | macs = AntColonySystem(graph, ants_num=10, max_iter=1, alpha=alpha, beta=beta, q0=q0, k1=0, k2=0, apply_local_search=False) 19 | final_cost, _, _, penalty, _, _, _ = macs._ACS_DIST_G() 20 | 21 | has_zero_penalty = all(p == 0 for p in penalty) if isinstance(penalty, list) else penalty == 0 22 | 23 | results.append({ 24 | 'file': os.path.basename(file_path), 25 | 'alpha': alpha, 26 | 'beta': beta, 27 | 'q0': q0, 28 | 'rho': rho, 29 | 'cost': final_cost, 30 | 'penalty': penalty, 31 | 'has_zero_penalty': has_zero_penalty 32 | }) 33 | return results 34 | 35 | 36 | def find_best_params(results_df): 37 | zero_penalty_df = results_df[results_df['has_zero_penalty']] 38 | if not zero_penalty_df.empty: 39 | return zero_penalty_df.sort_values(by='cost').head(1)[['alpha', 'beta', 'q0', 'rho']] 40 | 41 | aggregated = results_df.groupby(['alpha', 'beta', 'q0', 'rho']).agg({'penalty_sum': 'mean', 'cost': 'mean'}).reset_index() 42 | return aggregated.sort_values(by=['penalty_sum', 'cost']).head(1)[['alpha', 'beta', 'q0', 'rho']] 43 | 44 | 45 | 46 | def print_and_save_best_combinations_for_alpha_beta_q0(csv_file, num_top_combinations=10, output_file='result_grid_search_best_alpha_beta_q0.csv'): 47 | df = pd.read_csv(csv_file) 48 | 49 | df['penalty'] = df['penalty'].apply(ast.literal_eval) 50 | df['penalty_sum'] = df['penalty'].apply(sum) 51 | 52 | top_combinations_list = [] 53 | 54 | for _, group_df in df.groupby('file'): 55 | top_combinations = group_df.sort_values(by=['penalty_sum', 'cost']).head(num_top_combinations) 56 | top_combinations_list.append(top_combinations) 57 | 58 | top_combinations_df = pd.concat(top_combinations_list) 59 | 60 | print("Top 10 combinations for each instance (minimizing penalty and cost):") 61 | print(top_combinations_df[['file', 'alpha', 'beta', 'q0', 'cost', 'penalty_sum']]) 62 | 63 | top_combinations_df.to_csv(output_file, index=False) 64 | 65 | 66 | def tuning_params(directory_path): 67 | abs_directory_path = os.path.abspath(directory_path) 68 | 69 | 70 | alpha_values = np.arange(1, 11) 71 | beta_values = np.arange(1, 11) 72 | q0_values = np.arange(0.1, 1.1, 0.1) 73 | rho_values = np.arange(0.1, 0.6, 0.1) 74 | 75 | num_cores = 5 76 | param_combinations = [(alpha, beta, q0, rho) 77 | for alpha in alpha_values 78 | for beta in beta_values 79 | for q0 in q0_values 80 | for rho in rho_values] 81 | 82 | chunks = [param_combinations[i::num_cores] for i in range(num_cores)] 83 | 84 | client = Client(n_workers=num_cores, threads_per_worker=1) 85 | futures = [] 86 | for filename in os.listdir(abs_directory_path): 87 | if filename.endswith(".txt"): 88 | file_path = os.path.join(abs_directory_path, filename) 89 | for chunk in chunks: 90 | future = client.submit(run_macs_for_alpha_beta_q0_rho, file_path, chunk) 91 | futures.append(future) 92 | 93 | results = [] 94 | for future in as_completed(futures): 95 | results.extend(future.result()) 96 | 97 | client.close() 98 | 99 | # Create DataFrame 100 | results_df = pd.DataFrame(results) 101 | 102 | # Check if 'penalty' is a list and calculate 'penalty_sum' accordingly 103 | if results_df['penalty'].apply(lambda x: isinstance(x, list)).all(): 104 | results_df['penalty_sum'] = results_df['penalty'].apply(sum) 105 | else: 106 | results_df['penalty_sum'] = results_df['penalty'] 107 | 108 | results_df.to_csv('grid_search_results_for_params.csv', index=False) 109 | 110 | print_and_save_best_combinations_for_alpha_beta_q0("grid_search_results_for_params.csv") 111 | 112 | best_params = find_best_params(results_df) 113 | print("Best parameters (alpha, beta, q0):") 114 | print(best_params) 115 | 116 | best_params.to_csv('best_parameters.csv', index=False) 117 | 118 | if __name__ == "__main__": 119 | tuning_params("../test_instances") 120 | -------------------------------------------------------------------------------- /EVRPTW/docs/ant.md: -------------------------------------------------------------------------------- 1 | # Module: ant 2 | 3 | ## Class: Ant 4 | 5 | The `Ant` class is designed to simulate an ant's journey in solving the Electric Vehicle Routing Problem with Time Windows (EVRPTW). 6 | 7 | ### Constructor 8 | 9 | ```python 10 | def __init__(self, graph: EvrptwGraph, start_index=0) 11 | # Initializes an `Ant` object. 12 | ``` 13 | #### Parameters: 14 | 15 | - `graph` (EvrptwGraph): The graph representing the EVRPTW problem. 16 | - `start_index` (int, default=0): The starting index in the graph, typically the depot. 17 | 18 | ### Static Methods 19 | 20 | #### `cal_total_travel_distance(graph: EvrptwGraph, travel_path: list) -> float` 21 | 22 | Calculates the total travel distance for a given path. 23 | 24 | - **Returns:** 25 | - `float`: The total travel distance of the path. 26 | 27 | ### Methods 28 | 29 | #### `update_eta_matrix(self)` 30 | 31 | Updates the eta matrix for routing decisions based on travel time and time window constraints. 32 | 33 | #### `move_to_next_index(self, next_index) -> bool` 34 | 35 | Attempts to move the ant to the specified next node index. 36 | 37 | - **Parameters:** 38 | - `next_index` (int): The index of the next node to move to. 39 | - **Returns:** 40 | - `bool`: True if the move was successful, False otherwise. 41 | 42 | #### `update_travel_state(self, next_index)` 43 | 44 | Updates the travel state of the ant when moving to a new node. 45 | 46 | - **Parameters:** 47 | - `next_index` (int): The index of the next node. 48 | 49 | #### `handle_depot_visit(self)` 50 | 51 | Handles the ant's visit to the depot. 52 | 53 | #### `handle_customer_visit(self, customer_index) -> bool` 54 | 55 | Handles the ant's visit to a customer node. 56 | 57 | - **Parameters:** 58 | - `customer_index` (int): The index of the customer node. 59 | - **Returns:** 60 | - `bool`: True if the visit is successful, False otherwise. 61 | 62 | #### `handle_station_visit(self)` 63 | 64 | Handles the ant's visit to a charging station. 65 | 66 | #### `has_enough_energy(self, current_index, next_index) -> bool` 67 | 68 | Checks if the ant has enough energy to move to the next node. 69 | 70 | - **Parameters:** 71 | - `current_index` (int): The current node index. 72 | - `next_index` (int): The next node index. 73 | - **Returns:** 74 | - `bool`: True if there is enough energy, False otherwise. 75 | 76 | #### `check_condition(self, next_index) -> bool` 77 | 78 | Checks if moving to the next node meets all constraints. 79 | 80 | - **Parameters:** 81 | - `next_index` (int): The index of the next node. 82 | - **Returns:** 83 | - `bool`: True if all constraints are met, False otherwise. 84 | 85 | #### `calculate_arrival_time(self, next_index) -> float` 86 | 87 | Calculates the arrival time at the next node. 88 | 89 | - **Parameters:** 90 | - `next_index` (int): The index of the next node. 91 | - **Returns:** 92 | - `float`: The estimated arrival time. 93 | 94 | #### `check_time_window(self, arrival_time, next_node) -> bool` 95 | 96 | Checks if the service at the next node can start within its time window. 97 | 98 | - **Parameters:** 99 | - `arrival_time` (float): The arrival time at the node. 100 | - `next_node` (Node): The node object. 101 | - **Returns:** 102 | - `bool`: True if service can start within the time window, False otherwise. 103 | 104 | #### `can_return_to_depot_from(self, next_index, current_time) -> bool` 105 | 106 | Checks if the ant can return to the depot in time from the next node. 107 | 108 | - **Parameters:** 109 | - `next_index` (int): The index of the next node. 110 | - `current_time` (float): The current time. 111 | - **Returns:** 112 | - `bool`: True if the ant can return to the depot in time, False otherwise. 113 | 114 | #### `return_to_depot(self)` 115 | 116 | Handles the logic for the ant to return to the depot. 117 | 118 | #### `check_capacity_constraint(self, next_index) -> bool` 119 | 120 | Checks if the ant's load capacity is not exceeded when visiting the next node. 121 | 122 | - **Parameters:** 123 | - `next_index` (int): The index of the next node. 124 | - **Returns:** 125 | - `bool`: True if capacity constraint is not violated, False otherwise. 126 | 127 | #### `cal_nearest_next_index(self, next_index_list) -> int` 128 | 129 | Calculates the nearest node index from the current position. 130 | 131 | - **Parameters:** 132 | - `next_index_list` (list): A list of node indices to consider. 133 | - **Returns:** 134 | - `int`: The index of the nearest node. 135 | 136 | #### `calculate_feasible_neighbors(self) -> list` 137 | 138 | Calculates feasible neighbors for the ant, excluding stations and the depot. 139 | 140 | - **Returns:** 141 | - `list`: A list of indices of feasible neighbor nodes. 142 | 143 | #### `is_feasible(self, path, ant) -> bool` 144 | 145 | Verifies if a given path is feasible for the EVRPTW. 146 | 147 | - **Parameters:** 148 | - `path` (list): The path to be checked. 149 | - `ant` (Ant): The ant object with methods to check constraints. 150 | - **Returns:** 151 | - `bool`: True if the path is feasible, False otherwise. 152 | 153 | #### `local_search_swap(self, graph, best_path) -> tuple` 154 | 155 | Performs a local search optimization by swapping nodes in the best path. 156 | 157 | - **Parameters:** 158 | - `graph` (EvrptwGraph): The graph representing the EVRPTW problem. 159 | - `best_path` (list): The current best path. 160 | - **Returns:** 161 | - A tuple containing the updated best path and its total travel distance. 162 | -------------------------------------------------------------------------------- /EVRPTW/results/comparisionResult.md: -------------------------------------------------------------------------------- 1 | ### Comparison of results between MACS-LS and ACSD-LS 2 | 3 |
4 | 5 | | E-VRPTW | MACS - ls mean | ACSD - ls mean | $\Delta$ \% | 6 | |---------|----------------|----------------|-------------| 7 | | C101-5 | 257.75 | N/A | 100 | 8 | | C103-5 | 184.50 | N/A | 100 | 9 | | **C206-5** | **242.55** | **242.55** | **0.0** | 10 | | **C208-5** | **158.48** | **158.48** | **0.0** | 11 | | R104-5 | 140.28 | 157.61 | 12.36 | 12 | | **R105-5** | **168.30** | **167.40** | **-0.53** | 13 | | R202-5 | 128.78 | 159.60 | 23.93 | 14 | | R203-5 | 194.57 | 205.92 | 5.83 | 15 | | RC105-5 | 241.30 | 243.15 | 0.76 | 16 | | RC108-5 | 253.93 | 253.93 | 0.00 | 17 | | RC204-5 | 185.16 | 185.16 | 0.00 | 18 | | RC208-5 | 167.98 | 186.21 | 10.85 | 19 | | C101-10 | 411.05 | N/A | 100 | 20 | | C104-10 | 278.02 | N/A | 100 | 21 | | **C202-10** | **322.29** | **305.19** | **-5.30** | 22 | | C205-10 | 266.18 | 293.26 | 10.17 | 23 | | R102-10 | 249.19 | 306.06 | 22.82 | 24 | | R103-10 | 212.14 | 214.14 | 0.94 | 25 | | **R201-10** | **269.97** | **252.48** | **-6.47** | 26 | | R203-10 | 232.68 | 319.07 | 37.12 | 27 | | **RC102-10**| **452.56** | **447.80** | **-1.05** | 28 | | RC108-10| 362.95 | 425.34 | 17.18 | 29 | | RC201-10| 412.86 | 418.89 | 1.46 | 30 | | RC205-10| 430.74 | N/A | 100 | 31 | | C103-15 | 425.71 | N/A | 100 | 32 | | C106-15 | 348.52 | N/A | 100 | 33 | | C202-15 | 512.77 | 593.72 | 15.78 | 34 | | C208-15 | 305.37 | 448.81 | 46.97 | 35 | | R102-15 | 429.64 | 440.27 | 2.47 | 36 | | R105-15 | 366.15 | 438.86 | 19.86 | 37 | | R202-15 | 457.54 | 499.97 | 9.27 | 38 | | R209-15 | 411.35 | 477.86 | 16.16 | 39 | | RC103-15| 403.56 | 557.61 | 38.17 | 40 | | RC108-15| 390.70 | 527.94 | 35.13 | 41 | | RC202-15| 486.85 | 499.97 | 2.69 | 42 | | RC204-15| 412.06 | 468.41 | 13.67 | 43 | 44 |
45 | 46 | _Note: "N/A" indicates that the instance does not generate eligible solutions._ 47 | 48 |
49 | 50 | ### Comparison of results between MACS+LS and ACSD+LS 51 | 52 |
53 | 54 | | E-VRPTW | MACS + ls mean | ACSD + ls mean | $\Delta$ \% | 55 | |---------|----------------|----------------|-------------| 56 | | C101-5 | 257.75 | N/A | 100 | 57 | | C103-5 | 176.05 | N/A | 100 | 58 | | **C206-5** | **242.55** | **242.55** | **0.00** | 59 | | **C208-5** | **158.48** | **158.48** | **0.00** | 60 | | R104-5 | 136.68 | 149.26 | 9.20 | 61 | | R105-5 | 156.08 | 157.06 | 0.67 | 62 | | R202-5 | 128.78 | 158.55 | 23.11 | 63 | | R203-5 | 179.06 | 199.16 | 11.21 | 64 | | RC105-5 | 241.30 | 245.30 | 1.66 | 65 | | RC108-5 | 253.93 | 256.67 | 1.07 | 66 | | RC204-5 | 185.15 | 187.94 | 1.50 | 67 | | RC208-5 | 167.98 | 183.59 | 9.29 | 68 | | C101-10 | 393.76 | N/A | 100 | 69 | | C104-10 | 273.93 | N/A | 100 | 70 | | C202-10 | 304.05 | 306.27 | 0.73 | 71 | | C205-10 | 228.28 | 292.73 | 28.23 | 72 | | R102-10 | 249.19 | 304.59 | 22.23 | 73 | | R103-10 | 207.05 | 214.54 | 3.61 | 74 | | R201-10 | 241.51 | 254.20 | 5.25 | 75 | | R203-10 | 219.54 | 321.87 | 46.61 | 76 | | RC102-10| 423.51 | 448.87 | 5.98 | 77 | | RC108-10| 345.92 | 420.43 | 21.53 | 78 | | RC201-10| 412.86 | 432.28 | 2.52 | 79 | | RC205-10| 328.78 | N/A | 100 | 80 | | C103-15 | 384.29 | N/A | 100 | 81 | | C106-15 | 275.13 | N/A | 100 | 82 | | C202-15 | 383.62 | 575.96 | 50.13 | 83 | | C208-15 | 300.55 | 442.39 | 47.19 | 84 | | R102-15 | 413.93 | 438.25 | 5.86 | 85 | | R105-15 | 336.15 | 400.76 | 19.22 | 86 | | R202-15 | 358.59 | 478.89 | 33.54 | 87 | | R209-15 | 318.92 | 477.49 | 49.72 | 88 | | RC103-15| 397.67 | 563.39 | 41.67 | 89 | | RC108-15| 371.05 | 499.83 | 34.70 | 90 | | RC202-15| 393.39 | 489.73 | 24.48 | 91 | | RC204-15| 391.04 | 456.38 | 16.70 | 92 | 93 |
94 | 95 | _Note: "N/A" indicates that the instance does not generate eligible solutions._ 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ant Colony Optimization for EVRPTW 🌐 2 | 3 | Welcome Ant Colony Optimization (ACO) project, where advanced algorithms and electric vehicles meet to solve the Electric Vehicle Routing Problem with Time Windows (EVRPTW). 🚚⏱️ 4 | 5 | ## Project Overview 📜 6 | 7 | This repository contains a Python-based implementation of ACO algorithms, designed to optimize routing paths for electric vehicles considering specific time windows and recharging requirements. 8 | 9 | ## Documentation 📖 10 | 11 | For more detailed information and usage instructions, check out our [documentation](/EVRPTW/docs/). 12 | 13 | ## Installation and Dependencies 🔧 14 | 15 | Ensure Python is installed along with the following packages: 16 | - `numpy` 17 | - `dask` 18 | - `pandas` 19 | - `yaml` 20 | - `csv` 21 | 22 | **Steps for Installation**: 23 | 24 | 1. **Clone the Repository**: 25 | - Use the command `git clone https://github.com/F-a-b-r-i-z-i-o/Ant_Colony_Optimization_for_Evrptw.git` to clone the EVRPTW repository to your local machine. 26 | 2. **Create and Activate Virtual Environment**: 27 | - Navigate to the EVRPTW directory (`cd EVRPTW`). 28 | - Create a virtual environment named `env` within this directory. Use the command `python3 -m venv env`. 29 | - Activate the virtual environment using the `script.sh` script with the command `. ./active_venv.sh`. 30 | 3. **Install Dependencies**: 31 | - Install required dependencies by executing `pip3 install -r requirements.txt`. This will install all the necessary packages as listed in the `requirements.txt` file. 32 | 33 | By following these steps, you'll set up the necessary environment to run the Ant Colony Optimization simulations for EVRPTW. 34 | 35 | ## Results 📊 36 | 37 | ### CPLEX vs MACS vs ACSD BEST VALUES FOUND 38 | 39 |
40 | 41 | | **E-VRPTW** | **CPLEX best** | **secs** | **MACS best** | **secs** | **ACSD best** | **secs** | **$\Delta\%$** | 42 | |----------|------------|------|-----------|-------|-----------|-------|------------| 43 | | C101-5 | 257.75 | 81 | 257.75 | 0.002 | N/A | 0.0 | 0.0 | 44 | | C103-5 | 176.05 | 5 | 176.05 | 0.21 | N/A | 0.0 | 0.0 | 45 | | **C206-5** | **242.55** | 518 | **242.55**| 0.008 | **242.55**| 0.47 | 0.0 | 46 | | **C208-5** | **158.48** | 15 | **158.48**| 0.0002| **158.48**| 0.037 | 0.0 | 47 | | R104-5 | 136.69 | 1 | 136.69 | 0.005 | 140.28 | 0.0351| 2.26 | 48 | | **R105-5** | **156.08** | 3 | **156.08** | 0.001 | **156.08** | 0.082 | 0.0 | 49 | | **R202-5** | **128.78** | 1 | **128.78** | 0.08 | **128.28** | 0.97 | 0.0 | 50 | | R203-5 | 179.06 | 5 | 179.06 | 1.11 | 197.77 | 1.79 | 10.44 | 51 | | **RC105-5**| **241.30** | 764 | **241.30** | 2.37 | **241.30** | 5.87 | 0.0 | 52 | | **RC108-5**| **253.93** | 311 | **253.93** | 0.002 | **253.93** | 0.057 | 0.0 | 53 | | RC204-5 | 176.39 | 54 | 176.39 | 0.001 | 179.81 | 0.089 | 1.93 | 54 | | RC208-5 | 167.98 | 21 | 167.98 | 0.003 | 181.67 | 0.034 | 8.15 | 55 | | C101-10 | 393.76 | 171 | 393.76 | 4.53 | N/A | 0.0 | 0.0 | 56 | | C104-10 | 273.93 | 360 | 273.93 | 24.1 | N/A | 0.0 | 0.0 | 57 | | **C202-10**| **304.06** | 300 | **304.06** | 2.85 | **304.06** | 4.90 | 0.0 | 58 | | C205-10 | 228.28 | 4 | 228.28 | 20.70 | 287.29 | 93.89 | 25.84 | 59 | | R102-10 | 249.19 | 389 | 249.19 | 1.57 | 268.87 | 20.87 | 7.89 | 60 | | **R103-10**| **207.05** | 119 | **207.05** | 13.50 | **207.05** | 35.68 | 0.0 | 61 | | R201-10 | 241.51 | 177 | 241.51 | 1.14 | 246.63 | 13.89 | 2.11 | 62 | | R203-10 | 218.21 | 573 | 218.21 | 15.45 | 302.78 | 56.89 | 38.75 | 63 | | **RC102-10**| **423.51** | 810 | **423.51** | 11.85 | **423.51** | 78.98 | 0.0 | 64 | | RC108-10 | 345.93 | 39 | 345.93 | 7.99 | 398.87 | 30.87 | 15.30 | 65 | | **RC201-10**| **412.86** | 7200 | **412.86** | 0.02 | **412.86** | 89.67 | 0.0 | 66 | | RC205-10 | 325.98 | 399 | 325.98 | 25.57 | N/A | 0.0 | 0.0 | 67 | | C103-15 | 348.29 | 7200 | 348.29 | 24.36 | N/A | 0.0 | 0.0 | 68 | | C106-15 | 275.13 | 17 | 275.13 | 21.88 | N/A | 0.0 | 0.0 | 69 | | C202-15 | 383.62 | 7200 | 383.62 | 59.46 | 555.58 | 363.68| 44.82 | 70 | | C208-15 | 300.55 | 5060 | 300.55 | 44.1 | 398.63 | 237.87| 32.63 | 71 | | R102-15 | 413.93 | 7200 | 413.93 | 25.84 | 415.15 | 234.68| 0.29 | 72 | | R105-15 | 336.15 | 7200 | 336.15 | 13.42 | 403.20 | 398.78| 19.95 | 73 | | R202-15 | 358.00 | 7200 | 358.00 | 7.32 | 400.26 | 287.89| 11.80 | 74 | | R209-15 | 313.24 | 7200 | 313.24 | 9.01 | 444.78 | 345.89| 41.99 | 75 | | RC103-15 | 397.67 | 7200 | 397.67 | 24.52 | 500.12 | 456.82| 25.76 | 76 | | RC108-15 | 370.25 | 7200 | 370.25 | 26.96 | 467.86 | 554.89| 26.36 | 77 | | RC202-15 | 394.39 | 7200 | 394.39 | 73.38 | 467.56 | 556.98| 18.55 | 78 | | RC204-15 | 407.45 | 7200 | 382.22 | 15.51 | 409.89 | 666.89| 7.23 | 79 | 80 |
81 | 82 | _Note: "N/A" indicates that the instance does not generate eligible solutions._ 83 | 84 | Other results are available [Results](/EVRPTW/results/) 85 | 86 | ## Path Visualization 🔄 87 | 88 | ![Path Evolution](/EVRPTW/gif/path_evolution.gif) 89 | 90 | ## Fitness Trend Graph 📉 91 | 92 | ![Fitness Trend](/EVRPTW/img/fitness_result_Run_2.png) 93 | 94 | ## Paper Citations 📄 95 | - Michalis Mavrovouniotis. "A Multiple Ant Colony System for the Electric Vehicle Routing Problem with Time Windows". KIOS Research and Innovation Center of Excellence, Department of Electrical and Computer Engineering, University of Cyprus, Nicosia, Cyprus. [View Paper](https://ieeexplore.ieee.org/document/10022257). 96 | 97 | ## Contributing 🤝 98 | 99 | Contributions to enhance the project are welcome. Please feel free to fork the repository, make improvements, and submit pull requests. 100 | 101 | ## License 📄 102 | 103 | This project is released under [GPL-3.0 License](/LICENSE). 104 | 105 | --- 106 | 107 | *Enjoy 2F_* 108 | -------------------------------------------------------------------------------- /EVRPTW/ACO/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import dask 3 | import dask.dataframe as dd 4 | from dask.distributed import Client 5 | from dask import delayed 6 | from acsd import AntColonySystem 7 | from evrptw_config import EvrptwGraph 8 | import pandas as pd 9 | import yaml 10 | import csv 11 | 12 | 13 | def load_configs(general_config_file, aco_config_file) -> dict: 14 | configs = {} 15 | # Load general configuration 16 | with open(general_config_file, "r") as file: 17 | configs["general"] = yaml.safe_load(file) 18 | # Load ACO-specific configuration 19 | with open(aco_config_file, "r") as file: 20 | configs["aco"] = yaml.safe_load(file)["aco_config"] 21 | return configs 22 | 23 | 24 | def run_macs( 25 | run, file_path, max_iter, ants_num, alpha, beta, q0, rho, k1, k2, apply_local_search 26 | ) -> tuple[list, list]: 27 | graph = EvrptwGraph(file_path, rho) 28 | macs_instance = AntColonySystem( 29 | graph, ants_num, max_iter, alpha, beta, q0, k1, k2, apply_local_search 30 | ) 31 | ( 32 | _, 33 | c_test_history, 34 | time_history, 35 | penalty_history, 36 | improvement_iter_history, 37 | num_vehicles, 38 | improvement_path, 39 | ) = macs_instance._ACS_DIST_G(apply_local_search) 40 | 41 | path_details = [] 42 | for idx, path in enumerate(improvement_path): 43 | node_types = [graph.nodes[idx].node_type for idx in path] 44 | # Convert path indices to coordinates 45 | coordinates = graph.get_coordinates_from_path(path) 46 | path_detail = { 47 | "run": run, 48 | "numer_of_improvement": idx, 49 | "path": path, 50 | "node_types": node_types, 51 | "coordinates": coordinates, 52 | "num_vehicles": num_vehicles, # Assuming num_vehicles is a list with the count per iteration 53 | } 54 | path_details.append(path_detail) 55 | 56 | # List to store improvements data for each run 57 | improvements = [] 58 | for idx, improvement_time in enumerate(improvement_iter_history): 59 | improvement_data = { 60 | "run": run, 61 | "improvement_iteration": improvement_time, 62 | "fitness_improvement": c_test_history[idx], 63 | "penalty": penalty_history[idx], 64 | "time": time_history[idx], 65 | "num_vehicles": num_vehicles, 66 | "path": improvement_path[idx], 67 | } 68 | improvements.append(improvement_data) 69 | 70 | return improvements, path_details 71 | 72 | 73 | def save_results_to_csv(results, filename) -> None: 74 | df = pd.DataFrame(results) 75 | dask_df = dd.from_pandas(df, npartitions=1) 76 | dask_df.to_csv(filename, index=False, single_file=True) 77 | 78 | 79 | def save_path_details_to_csv(path_details, filename) -> None: 80 | df = pd.DataFrame(path_details) 81 | df["coordinates"] = df["coordinates"].apply( 82 | lambda coords: ";".join([f"({x},{y})" for x, y in coords]) 83 | ) 84 | dask_df = dd.from_pandas(df, npartitions=1) 85 | dask_df.to_csv(filename, index=False, single_file=True) 86 | 87 | 88 | def calculate_and_append_average(filename) -> None: 89 | df = pd.read_csv(filename) 90 | last_improvements = df.groupby("run").last()["fitness_improvement"] 91 | # Calculate the average of these last improvements 92 | avg_fitness = last_improvements.mean() 93 | 94 | with open(filename, "a", newline="") as file: 95 | writer = csv.writer(file) 96 | writer.writerow(["Average Fitness of Last Iterations", avg_fitness]) 97 | 98 | 99 | if __name__ == "__main__": 100 | # Define directory paths 101 | directory_path = "paper_total_instances" 102 | abs_directory_path = os.path.join( 103 | os.path.dirname(os.path.abspath(__file__)), directory_path 104 | ) 105 | results_directory = os.path.join( 106 | os.path.dirname(os.path.abspath(__file__)), "results" 107 | ) 108 | 109 | # Load configuration files 110 | general_config_file = "config/dask_config.yaml" 111 | aco_config_file = "config/aco_config.yaml" 112 | configs = load_configs(general_config_file, aco_config_file) 113 | 114 | # Extract ACO configuration settings 115 | aco_config = configs["aco"] 116 | num_runs = aco_config["num_runs"] 117 | ants_num = aco_config["ants_num"] 118 | max_iter = aco_config["max_iter"] 119 | alpha = aco_config["alpha"] 120 | beta = aco_config["beta"] 121 | q0 = aco_config["q0"] 122 | k1 = aco_config["k1"] 123 | k2 = aco_config["k2"] 124 | rho = aco_config["rho"] 125 | apply_local_search = aco_config["apply_local_search"] 126 | save_path_details = aco_config["save_path_details"] 127 | 128 | # Initialize Dask client with configuration 129 | dask_config = configs["general"]["dask_config"] 130 | client = Client(**dask_config) 131 | 132 | # Process each file in the directory 133 | for filename in sorted(os.listdir(abs_directory_path)): 134 | if filename.endswith(".txt"): 135 | file_path = os.path.join(abs_directory_path, filename) 136 | instance_folder = os.path.join( 137 | results_directory, filename.replace(".txt", "") 138 | ) 139 | if not os.path.exists(instance_folder): 140 | os.makedirs(instance_folder) 141 | 142 | delayed_runs = [] 143 | 144 | # Prepare run with MACS config 145 | for i in range(num_runs): 146 | delayed_run = delayed(run_macs)( 147 | i, 148 | file_path, 149 | max_iter, 150 | ants_num, 151 | alpha, 152 | beta, 153 | q0, 154 | rho, 155 | k1, 156 | k2, 157 | apply_local_search, 158 | ) 159 | delayed_runs.append(delayed_run) 160 | 161 | # Compute all results in parallel 162 | all_improvements, all_path_details = zip(*dask.compute(*delayed_runs)) 163 | 164 | # Flatten the results for CSV and extract last improvements for average calculation 165 | combined_improvements = [] 166 | for improvement in all_improvements: 167 | combined_improvements.extend(improvement) 168 | 169 | # Save all results to CSV and calculate average of last iterations 170 | csv_filename = os.path.join(instance_folder, "fitness_result.csv") 171 | save_results_to_csv(combined_improvements, csv_filename) 172 | calculate_and_append_average(csv_filename) 173 | print(f"File {csv_filename} updated.") 174 | 175 | # Save path details to CSV 176 | if save_path_details: 177 | combined_path_details = [] 178 | for path_detail in all_path_details: 179 | combined_path_details.extend(path_detail) 180 | path_details_filename = os.path.join( 181 | instance_folder, "path_results.csv" 182 | ) 183 | save_path_details_to_csv(combined_path_details, path_details_filename) 184 | print(f"File {path_details_filename} updated.") 185 | 186 | # Close the Dask client 187 | client.close() 188 | -------------------------------------------------------------------------------- /EVRPTW/tests/test_evrptw_config.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import numpy as np 4 | import tempfile 5 | # Set up the script directory and the absolute file path 6 | script_dir = os.path.dirname(__file__) 7 | file_path = os.path.join(script_dir, '..', 'ACO', 'paper_total_instances', 'c103C5.txt') 8 | file_path = os.path.abspath(file_path) 9 | 10 | # Add the ACO directory to sys.path to ensure imports work 11 | aco_dir = os.path.join(script_dir, '..', 'ACO') 12 | sys.path.insert(0, aco_dir) 13 | 14 | from target import Node 15 | from evrptw_config import EvrptwGraph 16 | import pytest 17 | 18 | # Sample content of an EVRPTW instance file 19 | SAMPLE_INSTANCE = """ 20 | D0 d 0 0 0 0 0 0 21 | F1 f 1 1 0 0 0 0 22 | C1 c 2 2 1 10 20 30 23 | Vehicle fuel tank capacity: 100.0 24 | Vehicle load capacity: 200.0 25 | Vehicle fuel consumption rate: 1.0 26 | Vehicle inverse refueling rate: 2.0 27 | Vehicle average Velocity: 60.0 28 | """ 29 | # Fixture for creating an example graph 30 | @pytest.fixture 31 | def example_graph(): 32 | nodes = [ 33 | Node(0, "D0", "d", 0, 0, 0, 0, 0, 0), # Depot 34 | Node(1, "C1", "c", 1, 1, 5, 8, 10, 1), # Customer with time window and demand 35 | Node(2, "S1", "f", 2, 2, 0, 0, 0, 0) # Charging station 36 | ] 37 | graph = EvrptwGraph(file_path) 38 | graph.nodes = nodes 39 | graph.node_num = len(nodes) 40 | graph.node_dist_mat = np.array([ 41 | [0, 1.5, 3], 42 | [1.5, 0, 1.5], 43 | [3, 1.5, 0] 44 | ]) 45 | graph.tank_capacity = 10 46 | graph.load_capacity = 50 47 | graph.velocity = 1 48 | graph.fuel_consumption_rate = 1 49 | graph.charging_rate = 5 50 | return graph 51 | 52 | def test_global_update_pheromone(example_graph): 53 | # Test the global pheromone update method with a given best path and distance 54 | best_path = [0, 1, 2, 3, 4] 55 | best_path_distance = 10 56 | 57 | # Perform the global pheromone update 58 | example_graph.global_update_pheromone(best_path, best_path_distance) 59 | 60 | # Verify the pheromone levels are updated correctly for each edge in the best path 61 | for i in range(len(best_path) - 1): 62 | current_ind = best_path[i] 63 | next_ind = best_path[i + 1] 64 | # Calculate the expected pheromone level for the edge 65 | updated_pheromone = example_graph.tau_0 * (1 - example_graph.rho) + example_graph.rho / best_path_distance 66 | # Assert that the pheromone level matches the expected value 67 | assert example_graph.pheromone_mat[current_ind][next_ind] == pytest.approx(updated_pheromone) 68 | 69 | 70 | def test_read_instance(): 71 | # Create a temporary file with the sample instance data 72 | with tempfile.NamedTemporaryFile(mode='w+t', delete=False) as temp_file: 73 | temp_file.write(SAMPLE_INSTANCE) 74 | temp_file.seek(0) 75 | file_path = temp_file.name 76 | 77 | # Unpack the tuple returned by _read_instance 78 | node_num, nodes, node_dist_mat, fuel_stations, depot, tank_capacity, load_capacity, fuel_consumption_rate, charging_rate, velocity = EvrptwGraph._read_instance(file_path) 79 | 80 | # Verify that the extracted values match the expected values 81 | assert node_num == 3 # Adjust according to the SAMPLE_INSTANCE content 82 | assert len(fuel_stations) == 1 83 | assert depot.string_id == "D0" 84 | assert tank_capacity == 100.0 85 | assert load_capacity == 200.0 86 | assert fuel_consumption_rate == 1.0 87 | assert charging_rate == 2.0 88 | assert velocity == 60.0 89 | 90 | @pytest.fixture 91 | def graph_for_distance_matrix(): 92 | nodes = [ 93 | Node(0, "A", "a", 0, 0, 0, 0, 0, 0), 94 | Node(1, "B", "b", 3, 0, 0, 0, 0, 0), 95 | Node(2, "C", "c", 0, 4, 0, 0, 0, 0) 96 | ] 97 | return nodes 98 | 99 | def test_calculate_distance_matrix(graph_for_distance_matrix): 100 | distance_matrix = EvrptwGraph.calculate_distance_matrix(graph_for_distance_matrix) 101 | assert np.array_equal(distance_matrix, np.array([ 102 | [1e-9, 3.0, 4.0], 103 | [3.0, 1e-9, 5.0], 104 | [4.0, 5.0, 1e-9] 105 | ])) 106 | 107 | 108 | @pytest.fixture 109 | def graph_with_stations(): 110 | nodes = [ 111 | Node(0, "C0", "c", 0, 0, 0, 0, 0, 0), 112 | Node(1, "S1", "f", 1, 1, 0, 0, 0, 0), # Station 113 | Node(2, "C2", "c", 2, 2, 0, 0, 0, 0), 114 | Node(3, "S3", "f", 3, 3, 0, 0, 0, 0) # Another Station 115 | ] 116 | graph = EvrptwGraph(file_path) # Assuming the constructor can be called without file_path 117 | graph.nodes = nodes 118 | graph.node_num = len(nodes) 119 | graph.node_dist_mat = np.array([ 120 | [0, 1.0, 2.0, 3.0], 121 | [1.0, 0, 1.0, 2.0], 122 | [2.0, 1.0, 0, 1.0], 123 | [3.0, 2.0, 1.0, 0] 124 | ]) 125 | return graph 126 | 127 | def test_select_closest_station_with_stations(graph_with_stations): 128 | station_idx, total_distance = graph_with_stations.select_closest_station(0, 2) 129 | assert station_idx == 1 # Expected closest station index 130 | assert total_distance == 2.0 # Expected total distance to the station 131 | 132 | def test_select_closest_station_no_stations(): 133 | graph_without_stations = EvrptwGraph(file_path) 134 | graph_without_stations.nodes = [ 135 | Node(0, "C0", "c", 0, 0, 0, 0, 0, 0), 136 | Node(1, "C1", "c", 1, 1, 0, 0, 0, 0) 137 | ] 138 | graph_without_stations.node_dist_mat = np.array([ 139 | [0, 1.0], 140 | [1.0, 0] 141 | ]) 142 | 143 | station_idx, total_distance = graph_without_stations.select_closest_station(0, 1) 144 | assert station_idx == -1 # Expecting -1 as no station is found 145 | assert total_distance is None # Expecting None as no station is found 146 | 147 | @pytest.fixture 148 | def simple_graph(): 149 | nodes = [ 150 | Node(0, "D0", "d", 0, 0, 0, 0, 0, 0), # Depot 151 | Node(1, "C1", "c", 1, 1, 1, 0, 0, 0), # Customer 152 | Node(2, "S1", "f", 2, 2, 0, 0, 0, 0) # Charging station 153 | ] 154 | graph = EvrptwGraph(file_path) # Assuming the constructor can be called without parameters 155 | graph.nodes = nodes 156 | graph.node_num = len(nodes) 157 | graph.node_dist_mat = np.array([ 158 | [0, 1.0, 2.0], 159 | [1.0, 0, 1.0], 160 | [2.0, 1.0, 0] 161 | ]) 162 | graph.tank_capacity = 30 163 | graph.load_capacity = 5 164 | graph.velocity = 1 165 | graph.fuel_consumption_rate = 0.5 166 | graph.charging_rate = 1 167 | return graph 168 | 169 | 170 | def test_nearest_neighbor_heuristic_basic(simple_graph): 171 | travel_path, travel_distance, vehicle_num = simple_graph.nearest_neighbor_heuristic() 172 | 173 | # Adjust expected path and distance based on the logic of the method and the setup of simple_graph 174 | expected_path = [0, 2, 0] # Adjust if needed 175 | expected_distance = 4.0 # Adjust if needed 176 | 177 | assert travel_path == expected_path 178 | assert travel_distance == expected_distance 179 | assert vehicle_num == 1 # Assuming one vehicle is enough for this simple scenario 180 | 181 | 182 | def test_cal_nearest_next_index(simple_graph): 183 | index_to_visit = [1, 2] # Adjust indices based on test_graph's setup 184 | current_index = 0 185 | current_battery = simple_graph.tank_capacity 186 | current_time = 0 187 | 188 | expected_index = 2 # Adjust this based on your test_graph's setup 189 | next_index = simple_graph._cal_nearest_next_index(index_to_visit, current_index, current_battery, current_time) 190 | 191 | assert next_index == expected_index 192 | 193 | 194 | 195 | -------------------------------------------------------------------------------- /EVRPTW/tests/test_ant.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import numpy as np 4 | # Set up the script directory and the absolute file path 5 | script_dir = os.path.dirname(__file__) 6 | file_path = os.path.join(script_dir, '..', 'ACO', 'paper_total_instances', 'c103C5.txt') 7 | file_path = os.path.abspath(file_path) 8 | 9 | # Add the ACO directory to sys.path to ensure imports work 10 | aco_dir = os.path.join(script_dir, '..', 'ACO') 11 | sys.path.insert(0, aco_dir) 12 | 13 | from target import Node 14 | from evrptw_config import EvrptwGraph 15 | from ant import Ant 16 | import pytest 17 | 18 | 19 | @pytest.fixture 20 | def example_graph(): 21 | nodes = [ 22 | Node(0, "D0", "d", 0, 0, 0, 0, 0, 0), # Depot 23 | Node(1, "C1", "c", 1, 1, 5, 8, 10, 1), # Customer with time window and demand 24 | Node(2, "S1", "f", 2, 2, 0, 0, 0, 0) # Charging station 25 | ] 26 | graph = EvrptwGraph(file_path) 27 | graph.nodes = nodes 28 | graph.node_num = len(nodes) 29 | graph.node_dist_mat = np.array([ 30 | [0, 1.5, 3], 31 | [1.5, 0, 1.5], 32 | [3, 1.5, 0] 33 | ]) 34 | graph.tank_capacity = 10 35 | graph.load_capacity = 50 36 | graph.velocity = 1 37 | graph.fuel_consumption_rate = 1 38 | graph.charging_rate = 5 39 | # Ensure the depot's due date allows for the return from any node 40 | graph.nodes[0].due_date = 100 # Set a sufficiently large due date for the depot 41 | return graph 42 | 43 | 44 | def test_successful_move_to_customer(example_graph): 45 | ant = Ant(example_graph) 46 | assert ant.move_to_next_index(1) is True 47 | assert ant.current_index == 1 48 | assert ant.vehicle_load == 5 # Assuming the demand at customer node is 5 49 | 50 | def test_move_to_charging_station(example_graph): 51 | ant = Ant(example_graph) 52 | ant.fuel_level = 5 # Set fuel level low 53 | assert ant.move_to_next_index(2) is True 54 | assert ant.current_index == 2 # Ant should move to the charging station 55 | 56 | def test_failed_move_insufficient_energy(example_graph): 57 | ant = Ant(example_graph) 58 | ant.fuel_level = 0.5 # Set fuel very low, not enough to move anywhere 59 | assert ant.move_to_next_index(1) is False # Cannot reach customer 60 | assert ant.move_to_next_index(2) is False # Cannot reach charging station 61 | 62 | def test_failed_move_exceeds_load_capacity(example_graph): 63 | ant = Ant(example_graph) 64 | # Set the vehicle load to 46 or more, so adding the customer's demand exceeds the load capacity 65 | ant.vehicle_load = 46 66 | # Attempt to move to customer node 1 should fail as it would exceed load capacity 67 | assert ant.move_to_next_index(1) is False 68 | 69 | def test_move_to_customer(example_graph): 70 | ant = Ant(example_graph) 71 | ant.vehicle_load = 10 72 | initial_travel_distance = ant.total_travel_distance 73 | initial_travel_time = ant.vehicle_travel_time 74 | 75 | ant.update_travel_state(1) # Move to customer node 1 76 | 77 | assert ant.vehicle_load == 15 # Load should increase by customer's demand (5) 78 | assert ant.total_travel_distance > initial_travel_distance # Travel distance should increase 79 | assert ant.vehicle_travel_time > initial_travel_time # Travel time should increase 80 | assert 1 not in ant.index_to_visit # Customer node should be removed from index_to_visit 81 | 82 | def test_move_to_charging_station(example_graph): 83 | ant = Ant(example_graph) 84 | initial_fuel_level = ant.fuel_level 85 | 86 | ant.update_travel_state(2) # Move to charging station node 2 87 | 88 | assert ant.fuel_level >= initial_fuel_level # Fuel level should increase 89 | assert ant.vehicle_load == 0 # Load should not change 90 | 91 | def test_move_to_depot(example_graph): 92 | ant = Ant(example_graph) 93 | ant.vehicle_load = 10 94 | ant.fuel_level = 5 95 | 96 | ant.update_travel_state(0) # Move to depot node 0 97 | 98 | assert ant.fuel_level == example_graph.tank_capacity # Fuel level should be reset to tank capacity 99 | assert ant.vehicle_load == 0 # Load should be reset to 0 100 | assert ant.vehicle_travel_time == 0 # Travel time should be reset to 0 101 | 102 | def test_sufficient_energy_to_move(example_graph): 103 | ant = Ant(example_graph) 104 | ant.fuel_level = 10 # Set a sufficient fuel level 105 | assert ant.has_enough_energy(0, 1) is True # Assuming the distance to node 1 is within fuel range 106 | 107 | def test_insufficient_energy_no_station(example_graph): 108 | ant = Ant(example_graph) 109 | ant.fuel_level = 1 # Set a low fuel level 110 | assert ant.has_enough_energy(0, 1) is False # Assuming there's no reachable charging station 111 | 112 | def test_current_index_at_charging_station(example_graph): 113 | ant = Ant(example_graph) 114 | ant.fuel_level = 1 # Set a low fuel level 115 | current_index = 2 # Assuming node 2 is a charging station 116 | assert ant.has_enough_energy(current_index, 1) is True # Should return True as it assumes refueling 117 | 118 | def test_single_visit_constraint(example_graph): 119 | ant = Ant(example_graph) 120 | ant.travel_path = [1] # Assume node 1 has already been visited 121 | assert ant.check_condition(1) is False # Attempt to revisit node 1 122 | 123 | def test_capacity_constraint(example_graph): 124 | ant = Ant(example_graph) 125 | ant.vehicle_load = example_graph.load_capacity 126 | assert ant.check_condition(1) is False # Next move exceeds load capacity 127 | 128 | def test_energy_constraint(example_graph): 129 | ant = Ant(example_graph) 130 | ant.fuel_level = 0 # No fuel left 131 | assert ant.check_condition(1) is False # Insufficient energy to move 132 | 133 | def test_time_window_constraint(example_graph): 134 | ant = Ant(example_graph) 135 | ant.vehicle_travel_time = 100 # Set a high travel time 136 | assert ant.check_condition(1) is False # Cannot serve within time window 137 | 138 | def test_return_to_depot_feasibility(example_graph): 139 | ant = Ant(example_graph) 140 | ant.vehicle_travel_time = example_graph.nodes[0].due_date # Set travel time to depot's due time 141 | assert ant.check_condition(1) is False # Cannot return to depot in time 142 | 143 | def test_all_constraints_met(example_graph): 144 | ant = Ant(example_graph) 145 | ant.fuel_level = 10 # Sufficient fuel 146 | ant.vehicle_load = 0 # No load 147 | assert ant.check_condition(1) is True # All constraints are met 148 | 149 | def test_arrival_time_calculation(example_graph): 150 | ant = Ant(example_graph) 151 | ant.vehicle_travel_time = 5 # Assume a travel time of 5 units 152 | ant.current_index = 0 # Starting from index 0 153 | next_index = 1 # Moving to index 1 154 | 155 | # Calculate expected arrival time 156 | distance_to_next_index = example_graph.node_dist_mat[0][1] # Distance from index 0 to 1 157 | expected_arrival_time = ant.vehicle_travel_time + distance_to_next_index / example_graph.velocity 158 | 159 | assert ant.calculate_arrival_time(next_index) == expected_arrival_time 160 | 161 | def test_check_time_window(example_graph): 162 | ant = Ant(example_graph) 163 | arrival_time = 5 # Example arrival time 164 | next_node = example_graph.nodes[1] # Example node 165 | 166 | # Assuming next_node has specific ready_time and due_date 167 | assert ant.check_time_window(arrival_time, next_node) == (True, 0) # Adjust according to expected values 168 | 169 | 170 | def test_return_to_depot(example_graph): 171 | ant = Ant(example_graph) 172 | ant.current_index = 1 # Starting from an example index 173 | ant.return_to_depot() 174 | 175 | assert ant.current_index == 0 # Ant should now be at the depot 176 | assert ant.vehicle_load == 0 # Load should be reset 177 | assert ant.fuel_level == example_graph.tank_capacity # Fuel should be refilled 178 | 179 | 180 | def test_can_return_to_depot_in_time(example_graph): 181 | ant = Ant(example_graph) 182 | current_time = 90 # Set a current time where returning to the depot is possible 183 | next_index = 1 # Choose a node close to the depot 184 | 185 | # Test if the ant can return to the depot from the next index in time 186 | assert ant.can_return_to_depot_from(next_index, current_time) == True 187 | 188 | def test_cannot_return_to_depot_in_time(example_graph): 189 | ant = Ant(example_graph) 190 | current_time = 98 # Set a current time very close to the depot's due time 191 | next_index = 2 # Choose a node farther from the depot 192 | 193 | # Test if the ant cannot return to the depot from the next index in time 194 | assert ant.can_return_to_depot_from(next_index, current_time) == False 195 | 196 | def test_check_capacity_constraint(example_graph): 197 | ant = Ant(example_graph) 198 | ant.vehicle_load = 20 # Set a current load 199 | assert ant.check_capacity_constraint(1) == True # Assuming node 1's demand does not exceed capacity 200 | ant.vehicle_load = 49 # Set load close to capacity 201 | assert ant.check_capacity_constraint(1) == False # Assuming node 1's demand exceeds capacity 202 | 203 | def test_cal_nearest_next_index(example_graph): 204 | ant = Ant(example_graph) 205 | ant.current_index = 0 # Starting from depot 206 | next_index_list = [1, 2] 207 | assert ant.cal_nearest_next_index(next_index_list) == 1 # Assuming node 1 is closer to the depot than node 2 208 | 209 | def test_calculate_feasible_neighbors(example_graph): 210 | ant = Ant(example_graph) 211 | ant.vehicle_load = 20 # Set a current load 212 | feasible_neighbors = ant.calculate_feasible_neighbors() 213 | # Assuming the feasible neighbors meet all constraints and exclude charging stations and the depot 214 | assert all(index not in [0, 2] for index in feasible_neighbors) 215 | 216 | def test_cal_total_travel_distance_non_empty_path(example_graph): 217 | travel_path = [0, 1, 2] # A sample travel path 218 | # Manually calculate the expected total distance 219 | expected_distance = example_graph.node_dist_mat[0][1] + example_graph.node_dist_mat[1][2] 220 | assert Ant.cal_total_travel_distance(example_graph, travel_path) == expected_distance 221 | 222 | def test_is_feasible_with_feasible_path(example_graph): 223 | ant = Ant(example_graph) 224 | feasible_path = [0, 1, 2, 0] # Assumendo che questo sia un percorso fattibile 225 | assert ant.is_feasible(feasible_path, ant) is True 226 | 227 | def test_is_feasible_with_path_exceeding_load_capacity(example_graph): 228 | ant = Ant(example_graph) 229 | ant.vehicle_load = example_graph.load_capacity 230 | infeasible_path_due_to_load = [0, 1, 0] 231 | 232 | assert not ant.is_feasible(infeasible_path_due_to_load, ant) 233 | 234 | 235 | def test_is_feasible_not_ending_at_depot(example_graph): 236 | ant = Ant(example_graph) 237 | path_not_ending_at_depot = [0, 1, 2] # Does not end at depot 238 | assert ant.is_feasible(path_not_ending_at_depot, ant) is False 239 | 240 | def test_local_search_swap_improvement(example_graph): 241 | ant = Ant(example_graph) 242 | initial_path = [0, 1, 2, 0] # Sample initial path 243 | improved_path, improved_distance = ant.local_search_2opt(example_graph, initial_path) 244 | initial_distance = Ant.cal_total_travel_distance(example_graph, initial_path) 245 | 246 | assert improved_distance <= initial_distance 247 | assert ant.is_feasible(improved_path, ant) 248 | 249 | def test_local_search_swap_no_improvement(example_graph): 250 | ant = Ant(example_graph) 251 | path = [0, 1, 2, 0] # Path where no improvement is possible 252 | new_path, new_distance = ant.local_search_2opt(example_graph, path) 253 | initial_distance = Ant.cal_total_travel_distance(example_graph, path) 254 | 255 | assert new_distance == initial_distance 256 | assert new_path == path 257 | 258 | def test_local_search_swap_skips_depot(example_graph): 259 | ant = Ant(example_graph) 260 | path = [0, 1, 2, 0] 261 | new_path, _ = ant.local_search_2opt(example_graph, path) 262 | 263 | # Verify that depot (node 0) is not swapped 264 | assert new_path[0] == 0 and new_path[-1] == 0 265 | -------------------------------------------------------------------------------- /EVRPTW/ACO/acsd.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import random 3 | from evrptw_config import EvrptwGraph 4 | from ant import Ant 5 | import time 6 | 7 | 8 | class AntColonySystem: 9 | """ 10 | Implements a Multiple Ant Colony System for solving the Electric Vehicle Routing Problem 11 | with Time Windows (EVRPTW). 12 | 13 | Attributes: 14 | graph (EvrptwGraph): The graph representing the EVRPTW problem. 15 | ants_num (int): Number of ants in the system. 16 | max_iter (int): Maximum number of iterations. 17 | alpha (float): Parameter influencing the importance of pheromone. 18 | beta (float): Parameter influencing the importance of heuristic information. 19 | q0 (float): Parameter for pseudo-random proportional decision rule. 20 | k1 (float): Coefficient for time window penalty. 21 | k2 (float): Coefficient for the number of vehicles penalty. 22 | """ 23 | 24 | def __init__( 25 | self, 26 | graph: EvrptwGraph, 27 | ants_num=10, 28 | max_iter=400, 29 | alpha=1, 30 | beta=2, 31 | q0=0.9, 32 | k1=0, 33 | k2=0, 34 | apply_local_search=False, 35 | ): 36 | self.graph = graph 37 | self.ants_num = ants_num 38 | self.max_iter = max_iter 39 | self.beta = beta 40 | self.alpha = alpha 41 | self.q0 = q0 42 | self.k1 = k1 43 | self.k2 = k2 44 | self.apply_local_search = apply_local_search 45 | 46 | def _construct_solution(self, sigma: int) -> tuple[list, float]: 47 | """ 48 | Constructs a solution (path) for a given number of vehicles (sigma) based on the 49 | ant colony system and various constraints, including energy limits. 50 | 51 | The function iteratively builds a path by selecting nodes (customers, depot, charging stations) 52 | based on the feasibility and constraints like energy limits and time windows. It utilizes 53 | multiple vehicles if necessary, as specified by the parameter sigma. 54 | 55 | Args: 56 | - sigma (int): The number of vehicles available for constructing the solution. 57 | 58 | Returns: 59 | - tuple: 60 | - list: T_k, the constructed travel path that includes customers, depot, and possibly charging stations. 61 | - float: cumulative_time_window_penalty, the total penalty incurred due to time window violations 62 | over the entire constructed path. 63 | """ 64 | 65 | T_k = [self.graph.depot.idx] # Inizialize path from depot 66 | vehicles = 1 67 | visited_customers = set() 68 | all_customers = set( 69 | i for i, node in enumerate(self.graph.nodes) if node.is_customer() 70 | ) 71 | ants = [Ant(self.graph) for _ in range(self.ants_num)] 72 | cumulative_time_window_penalty = 0 73 | penalty_rate = 1.0 74 | 75 | while vehicles <= sigma and visited_customers != all_customers: 76 | i = T_k[-1] # Current Node 77 | ant = self.roulette_wheel_selection(ants) # Select ant 78 | ant.travel_path.clear() 79 | feasible_nodes = set(ant.calculate_feasible_neighbors()) - visited_customers 80 | remaining_customers = all_customers - feasible_nodes - visited_customers 81 | 82 | if feasible_nodes: 83 | j = self.select_next_index(ant, feasible_nodes) 84 | elif remaining_customers: 85 | j = self.select_next_index(ant, remaining_customers) 86 | else: 87 | # If there are no more clients to visit, return to the depot. 88 | j = self.graph.depot.idx 89 | vehicles += 1 # Start a new route with another vehicle. 90 | 91 | if j != self.graph.depot.idx: 92 | arrival_time_at_j = ant.calculate_arrival_time(j) 93 | next_node = self.graph.nodes[j] 94 | 95 | within_time_window, time_violation = ant.check_time_window( 96 | arrival_time_at_j, next_node 97 | ) 98 | 99 | if not within_time_window: 100 | # Calculates the penalty for exceeding the time window. 101 | penalty = time_violation * penalty_rate 102 | cumulative_time_window_penalty += penalty 103 | 104 | # Checks whether a charging station is needed to reach the next node j 105 | if not ant.has_enough_energy(i, j): 106 | s, _ = self.graph.select_closest_station(i, j) 107 | if T_k[-1] != s: 108 | T_k.append(s) 109 | ant.move_to_next_index(s) 110 | else: 111 | T_k.append(j) 112 | visited_customers.add(j) 113 | ant.move_to_next_index(j) 114 | 115 | if T_k[-1] != self.graph.depot.idx: # Check depot entry at the end 116 | i = T_k[-1] 117 | j = self.graph.depot.idx 118 | if not ant.has_enough_energy(i, j): 119 | # If not, select the closest charging station from the current node (i) to the depot (j) 120 | s, _ = self.graph.select_closest_station(i, j) 121 | if T_k[-1] != s: 122 | # If the last node in the path is not the charging station, append it 123 | T_k.append(s) 124 | ant.move_to_next_index(s) 125 | 126 | T_k.append(j) 127 | ant.move_to_next_index(j) 128 | else: 129 | T_k.append(j) 130 | ant.move_to_next_index(j) 131 | 132 | return T_k, cumulative_time_window_penalty 133 | 134 | def evaluate_solution(self, travel_path: list) -> float: 135 | """ 136 | Evaluate the total distance of a given travel path. 137 | 138 | Returns: 139 | - float: The total distance of the provided travel path. 140 | """ 141 | 142 | # Check if the graph is properly initialized 143 | if self.graph is None: 144 | raise ValueError("Graph not initialized in MultipleAntColonySystem") 145 | 146 | # Check if the path is empty and return 0 if it is 147 | if not travel_path: 148 | return 0 149 | 150 | # Initialize the distance variable 151 | distance = 0 152 | current_ind = travel_path[0] 153 | 154 | # Calculate the distance between consecutive nodes in the path 155 | for next_ind in travel_path[1:]: 156 | distance += self.graph.node_dist_mat[current_ind][next_ind] 157 | current_ind = next_ind 158 | 159 | # Add the distance from the last node in the path back to the depot 160 | distance += self.graph.node_dist_mat[current_ind][travel_path[0]] 161 | 162 | return distance 163 | 164 | def _ACS_DIST_G(self, apply_local_search=True) -> tuple: 165 | """ 166 | Performs the Ant Colony System algorithm for the Electric Vehicle Routing Problem with Time Windows (EVRPTW). 167 | This function iterates over a set number of ants, allowing each to construct a solution path. 168 | The best path found is then globally updated. 169 | 170 | Returns: 171 | tuple: Contains the final best path distance, the best path, and various histories 172 | (final cost, time, penalties, improvements). 173 | """ 174 | C_final = float("inf") 175 | T_best = None 176 | m = self.graph.vehicles 177 | start_time = time.time() 178 | C_final_history = [] 179 | time_history = [] 180 | penalty_history = [] 181 | improvement_iter_history = [] 182 | improved_path = [] 183 | 184 | # Initialize pheromone levels 185 | for i in range(self.graph.node_num): 186 | for j in range(self.graph.node_num): 187 | self.graph.pheromone_mat[i][j] = self.graph.tau_0 188 | 189 | for iteration in range(self.max_iter): 190 | ants = [Ant(self.graph) for _ in range(self.ants_num)] 191 | 192 | local_best_cost = float("inf") 193 | local_best_path = None 194 | local_best_penalty = None 195 | 196 | for ant in ants: 197 | T_ant, initial_penalty = self._construct_solution(m) 198 | 199 | # Apply local search if enabled 200 | if apply_local_search: 201 | T_ant, improved_path_distance = ant.local_search_2opt( 202 | self.graph, T_ant 203 | ) 204 | else: 205 | improved_path_distance = self.evaluate_solution(T_ant) 206 | 207 | num_vehicles = self.get_active_vei(T_ant) 208 | C_ant = ( 209 | improved_path_distance 210 | + (initial_penalty * self.k1) 211 | + (num_vehicles * self.k2) 212 | ) 213 | 214 | if C_ant < local_best_cost: 215 | local_best_cost = C_ant 216 | local_best_path = T_ant 217 | local_best_penalty = initial_penalty 218 | 219 | # Update global best path if a new local best is found 220 | if local_best_cost < C_final: 221 | elapsed_time = time.time() - start_time 222 | C_final = local_best_cost 223 | T_best = local_best_path 224 | C_final_history.append(C_final) 225 | time_history.append(elapsed_time) 226 | penalty_history.append(local_best_penalty) 227 | improved_path.append(T_best) 228 | improvement_iter_history.append(iteration) 229 | 230 | print( 231 | f"[Iteration {iteration}]: Found improved path with cost {C_final}. Time: {time_history[-1]:.3f} seconds" 232 | ) 233 | self.graph.global_update_pheromone(T_best, C_final) 234 | 235 | total_time = time.time() - start_time 236 | print( 237 | f"Final best path distance: {C_final}. Total time: {total_time:.3f} seconds" 238 | ) 239 | 240 | return ( 241 | C_final, 242 | C_final_history, 243 | time_history, 244 | penalty_history, 245 | improvement_iter_history, 246 | num_vehicles, 247 | improved_path, 248 | ) 249 | 250 | def select_next_index(self, ant: Ant, nodes: list) -> int: 251 | """ 252 | Selects the next index for an ant to visit based on the transition probabilities calculated from 253 | the pheromone trail and heuristic information. 254 | 255 | The selection strategy incorporates both exploitation (choosing the best option) and exploration 256 | (randomly choosing based on probability distribution) to balance between finding optimal paths 257 | and exploring new paths. 258 | 259 | Args: 260 | ant (Ant): The ant object which is currently constructing a path. 261 | nodes (list): A list of node indices that the ant can potentially visit next. 262 | 263 | Returns: 264 | int: The index of the next node the ant will move to. Returns None if there are no feasible nodes 265 | """ 266 | if not nodes: 267 | return None 268 | 269 | current_index = ant.current_index 270 | feasible_nodes = set(ant.calculate_feasible_neighbors()) 271 | next_index = None 272 | 273 | if nodes.issubset(feasible_nodes): 274 | # Calculate transition probabilities 275 | transition_probabilities = np.zeros(len(nodes)) 276 | for idx, j in enumerate(nodes): 277 | transition_probabilities[idx] = np.power( 278 | self.graph.pheromone_mat[current_index][j], self.alpha 279 | ) * np.power(ant.eta_k_ij_mat[current_index][j], self.beta) 280 | # Normalize the probabilities 281 | sum_probabilities = np.sum(transition_probabilities) 282 | normalized_probabilities = ( 283 | transition_probabilities / sum_probabilities 284 | if sum_probabilities > 0 285 | else None 286 | ) 287 | 288 | if normalized_probabilities is not None: 289 | if np.random.rand() < self.q0: 290 | # Exploitation: Choose the next node with the highest probability 291 | max_prob_index = np.argmax(normalized_probabilities) 292 | next_index = list(nodes)[max_prob_index] 293 | else: 294 | # Exploration: Choose the next node based on the probability distribution 295 | next_index = AntColonySystem.stochastic_accept( 296 | list(nodes), normalized_probabilities 297 | ) 298 | else: 299 | next_index = np.random.choice(list(nodes), p=normalized_probabilities) 300 | else: 301 | # If sum of probabilities is zero, select randomly 302 | next_index = np.random.choice(list(nodes)) 303 | 304 | return next_index 305 | 306 | @staticmethod 307 | def stochastic_accept(index_to_visit: list, transition_prob: list) -> int: 308 | """ 309 | Stochastic acceptance rule for selecting the next index to visit based on transition probabilities. 310 | 311 | Parameters: 312 | - index_to_visit (list): List of indices to potentially visit. 313 | - transition_prob (list): List of transition probabilities corresponding to each index. 314 | 315 | Returns: 316 | - int: The chosen index. 317 | """ 318 | 319 | # Calculate N and max fitness value 320 | N = len(index_to_visit) 321 | 322 | # Normalize the transition probabilities 323 | sum_tran_prob = np.sum(transition_prob) 324 | norm_transition_prob = transition_prob / sum_tran_prob 325 | 326 | # O(1) selection 327 | while True: 328 | # Randomly select an individual with uniform probability 329 | ind = int(N * random.random()) 330 | if random.random() <= norm_transition_prob[ind]: 331 | return index_to_visit[ind] 332 | 333 | def roulette_wheel_selection(self, ants: list) -> Ant: 334 | """ 335 | Perform roulette wheel selection to pick an ant based on the quality of its solution. 336 | 337 | The idea behind roulette wheel selection is that ants with better solutions (shorter paths) 338 | have a higher chance of getting selected. 339 | 340 | Returns: 341 | - Ant: The selected ant based on roulette wheel selection. 342 | """ 343 | 344 | # Calculate the total distance traveled by each ant 345 | path_lengths = [self.evaluate_solution(ant.travel_path) for ant in ants] 346 | 347 | # Convert path lengths to fitness values, handling zero or very small lengths 348 | fitness_values = [] 349 | for length in path_lengths: 350 | if length > 0: 351 | fitness_value = 1.0 / length 352 | else: 353 | # Assign a small fitness value to avoid division by zero 354 | fitness_value = ( 355 | 1e-8 # This can be adjusted based on your algorithm's needs 356 | ) 357 | fitness_values.append(fitness_value) 358 | 359 | # Compute the cumulative sum of the fitness values using numpy for efficiency 360 | cumulative_fitness = np.cumsum(fitness_values) 361 | 362 | # Select a random value in the range [0, total fitness value] 363 | rand_value = random.uniform(0, cumulative_fitness[-1]) 364 | 365 | # Use binary search to find the ant corresponding to the random value 366 | chosen_idx = np.searchsorted(cumulative_fitness, rand_value) 367 | 368 | return ants[chosen_idx] 369 | 370 | def get_active_vei(self, path: list) -> int: 371 | """ 372 | Calculates the number of active vehicles used in a given path. 373 | 374 | The method counts the occurrences of the depot (represented as '0' in the path) 375 | to determine the number of vehicles used. Each occurrence indicates the start 376 | or end of a vehicle's route. 377 | 378 | Args: 379 | - path (list): The path containing node indices, including the depot (0). 380 | 381 | Returns: 382 | - int: The number of active vehicles used in the path. 383 | """ 384 | vei = path.count(0) - 1 385 | return vei 386 | -------------------------------------------------------------------------------- /EVRPTW/ACO/evrptw_config.py: -------------------------------------------------------------------------------- 1 | from target import Node 2 | import numpy as np 3 | import re 4 | 5 | 6 | class EvrptwGraph: 7 | def __init__(self, file_path, rho=0.1): 8 | """ 9 | Initializes an EVRPTW graph from a given instance file. 10 | 11 | Parameters: 12 | file_path (str): Path to the EVRPTW instance file containing node and vehicle data. 13 | rho (float, optional): Pheromone evaporation rate for the ACO algorithm. Defaults to 0.1. 14 | 15 | The constructor performs the following initializations: 16 | - Reads data from the instance file to set up nodes, vehicle properties, and other parameters. 17 | - Calculates initial pheromone levels and sets up a pheromone matrix. 18 | - Implements the nearest neighbor heuristic to generate a preliminary solution. 19 | """ 20 | self.rho = rho 21 | ( 22 | self.node_num, 23 | self.nodes, 24 | self.node_dist_mat, 25 | self.fuel_stations, 26 | self.depot, 27 | self.tank_capacity, 28 | self.load_capacity, 29 | self.fuel_consumption_rate, 30 | self.charging_rate, 31 | self.velocity, 32 | ) = self._read_instance(file_path) 33 | 34 | self.nnh_travel_path, self.Cnn, self.vehicles = ( 35 | self.nearest_neighbor_heuristic() 36 | ) 37 | 38 | self.tau_0 = 1 / (self.node_num * self.Cnn) 39 | 40 | # Create pheromone matrix 41 | self.pheromone_mat = np.full((self.node_num, self.node_num), self.tau_0) 42 | 43 | def global_update_pheromone( 44 | self, best_path: list, best_path_distance: float 45 | ) -> None: 46 | """ 47 | Global update pheromone for the best-so-far ant. 48 | 49 | Parameters: 50 | - best_path (list): The best path found so far. 51 | - best_path_distance (float): The total distance of the best path. 52 | """ 53 | 54 | # Only evaporate pheromone on arcs that are part of the best path 55 | for i in range(len(best_path) - 1): 56 | current_ind = best_path[i] 57 | next_ind = best_path[i + 1] 58 | self.pheromone_mat[current_ind][next_ind] *= 1 - self.rho 59 | self.pheromone_mat[current_ind][next_ind] += self.rho / best_path_distance 60 | 61 | @staticmethod 62 | def calculate_distance_matrix(nodes: list) -> np.ndarray: 63 | """ 64 | Calculate the Euclidean distance matrix for a list of nodes. 65 | 66 | Parameters: 67 | - nodes (list): A list of Node objects. 68 | 69 | Returns: 70 | - numpy.ndarray: A distance matrix where element [i, j] represents 71 | the distance between nodes i and j. 72 | """ 73 | coordinates = np.array([(node.x, node.y) for node in nodes]) 74 | diff = coordinates[:, np.newaxis, :] - coordinates[np.newaxis, :, :] 75 | dist_mat = np.sqrt(np.sum(diff**2, axis=2)) 76 | dist_mat[dist_mat == 0] = 1e-9 # Avoid division by zero for distances of 0 77 | return dist_mat 78 | 79 | @staticmethod 80 | def _read_instance(file_path: str) -> tuple: 81 | """ 82 | Reads the EVRPTW instance from the given file and extracts necessary information. 83 | 84 | Parameters: 85 | - file_path (str): Path to the instance file. 86 | 87 | Returns: 88 | - tuple: Contains the number of nodes, list of nodes, distance matrix, 89 | list of fuel stations, depot, tank capacity, load capacity, 90 | fuel consumption rate, charging rate, and vehicle velocity. 91 | """ 92 | 93 | with open(file_path, "rt") as f: 94 | lines = f.readlines() 95 | nodes = [] 96 | fuel_stations = [] 97 | depot = None 98 | tank_capacity = 0.0 99 | load_capacity = 0.0 100 | fuel_consumption_rate = 0.0 101 | charging_rate = 0.0 102 | velocity = 0.0 103 | 104 | # Iterating over the lines in the file to extract information 105 | for line in lines[1:]: 106 | stl = line.split() 107 | 108 | # Extracting node information 109 | if len(stl) == 8: 110 | idx = int(stl[0][1:]) 111 | new_node = Node( 112 | idx, 113 | stl[0], 114 | stl[1], 115 | float(stl[2]), 116 | float(stl[3]), 117 | float(stl[4]), 118 | float(stl[5]), 119 | float(stl[6]), 120 | float(stl[7]), 121 | ) 122 | nodes.append(new_node) 123 | 124 | if stl[1] == "d": 125 | depot = new_node 126 | elif stl[1] == "f": 127 | fuel_stations.append(new_node) 128 | 129 | # Extracting vehicle related information 130 | elif "Vehicle fuel tank capacity" in line: 131 | tank_capacity = float(re.search(r"\d+\.\d+", line).group()) 132 | elif "Vehicle load capacity" in line: 133 | load_capacity = float(re.search(r"\d+\.\d+", line).group()) 134 | elif "fuel consumption rate" in line: 135 | fuel_consumption_rate = float(re.search(r"\d+\.\d+", line).group()) 136 | elif "inverse refueling rate" in line: 137 | charging_rate = float(re.search(r"\d+\.\d+", line).group()) 138 | elif "average Velocity" in line: 139 | velocity = float(re.search(r"\d+\.\d+", line).group()) 140 | 141 | # Calculating the distance matrix for the nodes 142 | node_num = len(nodes) 143 | 144 | # Use calculate_distance_matrix to create the distance matrix 145 | node_dist_mat = EvrptwGraph.calculate_distance_matrix(nodes) 146 | 147 | return ( 148 | node_num, 149 | nodes, 150 | node_dist_mat, 151 | fuel_stations, 152 | depot, 153 | tank_capacity, 154 | load_capacity, 155 | fuel_consumption_rate, 156 | charging_rate, 157 | velocity, 158 | ) 159 | 160 | def select_closest_station(self, i: int, j: int) -> (int, float): 161 | """ 162 | Select the charging station that is closest in terms of total distance to the nodes i and j. 163 | 164 | Parameters: 165 | - i (int): Index of the first node. 166 | - j (int): Index of the second node. 167 | 168 | Returns: 169 | - tuple: A tuple containing: 170 | - int: Index of the selected charging station. If no station is found, returns -1. 171 | - float: The total distance to the station. Returns None if no station is found. 172 | """ 173 | 174 | charging_station_indices = [ 175 | idx for idx, node in enumerate(self.nodes) if node.is_station() 176 | ] 177 | min_total_distance = float("inf") 178 | selected_station_idx = None 179 | 180 | for station_idx in charging_station_indices: 181 | distance_to_i = self.node_dist_mat[i][station_idx] 182 | distance_to_j = self.node_dist_mat[station_idx][j] 183 | total_distance = distance_to_i + distance_to_j 184 | 185 | if total_distance < min_total_distance: 186 | min_total_distance = total_distance 187 | selected_station_idx = station_idx 188 | 189 | if selected_station_idx is not None: 190 | return selected_station_idx, min_total_distance 191 | else: 192 | return -1, None 193 | 194 | def get_coordinates_from_path(self, path: list) -> list: 195 | """ 196 | Returns the (x, y) coordinates for each node in the given path. 197 | 198 | Parameters: 199 | - path (list): A list of node indices representing the path. 200 | 201 | Returns: 202 | - list: A list of tuples, each containing the (x, y) coordinates of a node in the path. 203 | """ 204 | coordinates = [] 205 | for node_idx in path: 206 | node = self.nodes[node_idx] 207 | coordinates.append((node.x, node.y)) 208 | return coordinates 209 | 210 | def create_node_type_map(self) -> dict: 211 | """ 212 | Creates a map that associates each node's index with its type. 213 | 214 | Returns: 215 | dict: A map associating each node's index (int) with its type (str). 216 | """ 217 | return {node.idx: node.node_type for node in self.nodes} 218 | 219 | def nearest_neighbor_heuristic(self, max_vehicle_num: int = None) -> tuple: 220 | """ 221 | Heuristic to generate a preliminary travel path using the nearest neighbor approach. 222 | 223 | Parameters: 224 | - max_vehicle_num (int, optional): The maximum number of vehicles to consider. 225 | 226 | Returns: 227 | tuple: A tuple containing the travel path (list), the total travel distance (float), 228 | and the number of vehicles used (int). 229 | """ 230 | index_to_visit = list( 231 | range(1, self.node_num) 232 | ) # Initialize with all nodes except depot (0) 233 | current_index = 0 234 | current_load = 0 235 | current_time = 0 236 | travel_distance = 0 237 | travel_path = [0] 238 | current_battery = self.tank_capacity 239 | 240 | if max_vehicle_num is None: 241 | max_vehicle_num = self.node_num 242 | 243 | while len(index_to_visit) > 0 and max_vehicle_num > 0: 244 | 245 | if ( 246 | self.nodes[current_index].is_station() 247 | and current_battery <= self.tank_capacity 248 | ): 249 | # Vehicle is currently at a charging station but hasn't fully charged 250 | current_battery += self.charging_rate 251 | else: 252 | if current_battery < self.tank_capacity: 253 | nearest_station_index, _ = self.select_closest_station( 254 | current_index, 0 255 | ) 256 | if ( 257 | nearest_station_index is not None 258 | and nearest_station_index != current_index 259 | ): 260 | # Go to the nearest charging station 261 | distance_to_station = self.node_dist_mat[current_index][ 262 | nearest_station_index 263 | ] 264 | travel_distance += distance_to_station 265 | travel_path.append(nearest_station_index) 266 | 267 | # Add travel time to station 268 | current_time += distance_to_station / self.velocity 269 | 270 | # Complete battery recharge 271 | current_battery += self.charging_rate 272 | current_index = nearest_station_index 273 | continue 274 | 275 | nearest_next_index = self._cal_nearest_next_index( 276 | index_to_visit, current_index, current_battery, current_time 277 | ) 278 | if nearest_next_index is not None: 279 | distance_to_next = self.node_dist_mat[current_index][nearest_next_index] 280 | battery_usage = distance_to_next * self.fuel_consumption_rate 281 | 282 | if current_battery >= battery_usage: 283 | if self.nodes[nearest_next_index].is_customer(): 284 | demand = self.nodes[nearest_next_index].demand 285 | if current_load <= self.load_capacity: 286 | # Update state to serve the node 287 | current_load += demand 288 | current_battery -= battery_usage 289 | travel_distance += distance_to_next 290 | travel_path.append(nearest_next_index) 291 | current_index = nearest_next_index 292 | index_to_visit.remove(nearest_next_index) 293 | else: 294 | # Can't serve this customer due to capacity or time window 295 | continue 296 | else: 297 | # For non-customer nodes, just move 298 | current_battery -= battery_usage 299 | travel_distance += distance_to_next 300 | travel_path.append(nearest_next_index) 301 | current_time += distance_to_next / self.velocity 302 | current_index = nearest_next_index 303 | index_to_visit.remove(nearest_next_index) 304 | 305 | else: 306 | # Not enough battery to reach next node, find and move to the nearest charging station 307 | nearest_station_index, _ = self.select_closest_station( 308 | current_index, 0 309 | ) 310 | if ( 311 | nearest_station_index is not None 312 | and nearest_station_index != current_index 313 | ): 314 | # Go to the nearest charging station 315 | distance_to_station = self.node_dist_mat[current_index][ 316 | nearest_station_index 317 | ] 318 | travel_distance += distance_to_station 319 | travel_path.append(nearest_station_index) 320 | 321 | # Add travel time to station and adjust battery 322 | current_time += distance_to_station / self.velocity 323 | current_battery = ( 324 | self.tank_capacity 325 | ) # Assuming immediate full recharge for simplicity 326 | current_index = nearest_station_index 327 | continue 328 | else: 329 | # If no charging station is available, return to depot 330 | distance_to_depot = self.node_dist_mat[current_index][0] 331 | travel_distance += distance_to_depot 332 | current_index = 0 333 | current_battery = self.tank_capacity # Recharge at depot 334 | current_time = 0 335 | current_load = 0 336 | max_vehicle_num -= 1 337 | else: 338 | distance_to_depot = self.node_dist_mat[current_index][0] 339 | travel_distance += distance_to_depot 340 | current_battery -= distance_to_depot * self.fuel_consumption_rate 341 | current_index = 0 342 | current_battery = self.tank_capacity # Recharge at depot 343 | current_time = 0 344 | current_load = 0 345 | # Return to depot and start with a new vehicle 346 | max_vehicle_num -= 1 347 | 348 | travel_path.append(0) 349 | vehicle_num = travel_path.count(0) - 1 350 | # print(travel_path, travel_distance) 351 | return travel_path, travel_distance, vehicle_num 352 | 353 | def _cal_nearest_next_index( 354 | self, 355 | index_to_visit: list, 356 | current_index: int, 357 | current_battery: float, 358 | current_time: float, 359 | ) -> int: 360 | """ 361 | Calculates the nearest next index that the vehicle can visit from the current index, considering battery and time constraints. 362 | 363 | Args: 364 | - index_to_visit (list): List of node indices that are yet to be visited. 365 | - current_index (int): The current node index. 366 | - current_battery (float): The current battery level. 367 | - current_time (float): The current time. 368 | 369 | Returns: 370 | int: The index of the nearest next node that can be visited. Returns None if no such node is found. 371 | """ 372 | 373 | nearest_ind = None 374 | nearest_distance = float("inf") 375 | 376 | for next_index in index_to_visit: 377 | dist = self.node_dist_mat[current_index][next_index] 378 | battery_usage = dist * self.fuel_consumption_rate 379 | 380 | # Check if there is enough battery to reach the next node. 381 | if current_battery >= battery_usage: 382 | if self.nodes[next_index].is_customer(): 383 | ready_time = self.nodes[next_index].ready_time 384 | due_date = self.nodes[next_index].due_date 385 | wait_time = max(ready_time - current_time - dist, 0) 386 | service_time = self.nodes[next_index].service_time 387 | current_time += dist + wait_time + service_time 388 | 389 | # Check whether the vehicle can serve the customer within its time window. 390 | if service_time >= due_date: 391 | continue 392 | 393 | # Calculate the distance back to the nearest charging station or to the depot after visiting the node. 394 | nearest_station_index, _ = self.select_closest_station(next_index, 0) 395 | dist_to_nearest_station = self.node_dist_mat[next_index][ 396 | nearest_station_index 397 | ] 398 | total_required_battery = ( 399 | battery_usage + dist_to_nearest_station * self.fuel_consumption_rate 400 | ) 401 | 402 | if current_battery >= total_required_battery: 403 | if dist < nearest_distance: 404 | nearest_distance = dist 405 | nearest_ind = next_index 406 | 407 | return nearest_ind 408 | --------------------------------------------------------------------------------