from ortools.sat.python import cp_model
import matplotlib.pyplot as plt # Data visualization
import random
import numpy as np
import pandas as pd
# parameters
data = {}
data['distance_matrix'] = [
[0, 2, 9, 10, 11],
[2, 0, 6, 4, 8],
[9, 6, 0, 7, 5],
[10, 4, 7, 0, 3],
[11, 8, 5, 3, 0]
]
data['demands'] = [0, 2, 4, 5, 6] # Demand at each location (including depot)
data['vehicle_capacities'] = [10,20,20] # Capacity of each vehicle
data['num_vehicles'] = len(data['vehicle_capacities'])
data['depot'] = 0
data['vehicle_costs'] = [4,3,7]
num_locations = len(data['distance_matrix'])
num_vehicles = data['num_vehicles']
depot = data['depot']
model = cp_model.CpModel()
# Variables
x = {}
for i in range(num_locations):
for j in range(num_locations):
for k in range(num_vehicles):
x[(i, j, k)] = model.NewBoolVar(f'x[{i},{j},{k}]')
u = {}
for i in range(num_locations):
u[i] = model.NewIntVar(0, sum(data['demands']), f'u[{i}]')
# Vehicle usage variable
vehicle_used = [model.NewBoolVar(f'vehicle_used[{k}]') for k in range(num_vehicles)]
# Constraints
# Each customer is visited exactly once by exactly one vehicle:
for i in range(1, num_locations):
model.Add(sum(x[(i, j, k)] for j in range(num_locations) for k in range(num_vehicles) if i != j) == 1)
for k in range(num_vehicles):
model.Add(sum(x[(depot, j, k)] for j in range(1, num_locations)) <= 1) # Each vehicle can leave the depot at most once:
model.Add(sum(x[(i, depot, k)] for i in range(1, num_locations)) <= 1) # Each vehicle can return to the depot at most once:
# If a vehicle is used, it must leave the start location
for k in range(num_vehicles):
expressions = sum(x[(depot, j, k)] for j in range(1, num_locations) )
model.Add(vehicle_used[k] == expressions )
# Flow conservation (each vehicle that enters a node must leave it):
for k in range(num_vehicles):
for h in range(num_locations):
model.Add(sum(x[(i, h, k)] for i in range(num_locations) if i != h) == sum(x[(h, j, k)] for j in range(num_locations) if h != j))
# Subtour elimination (MTZ constraints):
for k in range(num_vehicles):
for i in range(1, num_locations):
for j in range(1, num_locations):
if i != j:
model.Add(u[i] - u[j] + sum(data['demands']) * x[(i, j, k)] <= sum(data['demands']) - data['demands'][j])
# Vehicle capacity constraints:
for k in range(num_vehicles):
for i in range(1, num_locations):
model.Add(u[i] <= data['vehicle_capacities'][k])
#Load of the vehicle after visiting a node cannot exceed vehicle capacity:
for k in range(num_vehicles):
model.Add(sum(data['demands'][i] * sum(x[(i, j, k)] for j in range(num_locations) if i != j) for i in range(num_locations)) <= data['vehicle_capacities'][k])
# Objective: minimize the total distance traveled by all vehicles and minimize the total cost of vehicles used
objective_terms = []
for k in range(num_vehicles):
for i in range(num_locations):
for j in range(num_locations):
if i != j:
objective_terms.append(data['distance_matrix'][i][j] * x[(i, j, k)])
objective_terms.append(data['vehicle_costs'][k] * vehicle_used[k]) # Add the vehicle cost to the objective
model.Minimize(sum(objective_terms))
solver = cp_model.CpSolver()
status = solver.Solve(model)
all_routes = []
route_dic = {}
if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
print('Total distance = ', solver.ObjectiveValue())
for k in range(num_vehicles):
if solver.BooleanValue(vehicle_used[k]):
index = depot
route = [index]
while True:
next_index = -1
for j in range(num_locations):
if index != j and solver.BooleanValue(x[(index, j, k)]):
next_index = j
break
if next_index == depot or next_index == -1:
break
route.append(next_index)
index = next_index
route.append(depot)
d = []
print(f'Route for vehicle {k}: ' + ' -> '.join(map(str, route)))
all_routes.append(route)
route_dic[k] = route
cap = data['vehicle_capacities'][k]
print(f' cap: {cap} ')
for i in route:
d.append(data['demands'][i])
print(data['demands'][i])
cost = data['vehicle_costs'][k]
print(f'cost: {cost}')
print( f'total demand {sum(d)}' )
print('ends')
else:
print('No solution found!')