Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 6267c90

Browse files
fixes initial solution generation, project structure modified, the start of the bee colony dev
1 parent 7ddbef7 commit 6267c90

File tree

7 files changed

+444
-204
lines changed

7 files changed

+444
-204
lines changed

‎VehicleRoutingProblem.ipynb‎

Lines changed: 164 additions & 53 deletions
Large diffs are not rendered by default.

‎algorithm/bee_colony.py‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import random
2+
import numpy as np
3+
from copy import copy
4+
from utils import tools
5+
from random import randint
6+
from itertools import combinations
7+
from tqdm import tqdm, tqdm_notebook
8+
9+
from utils import tools
10+
from algorithm.base import Algorithm
11+
12+
13+
class BeeColony(Algorithm):
14+
15+
def __init__(self, problem):
16+
self.problem = problem
17+
18+
@property
19+
def name(self):
20+
return 'ArtificalBeeColony'
21+
22+
def solve(self):
23+
pass

‎algorithm/local_search.py‎

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1+
import random
12
import numpy as np
23
from copy import copy
3-
from utils import tools
44
from random import randint
55
from itertools import combinations
66
from tqdm import tqdm, tqdm_notebook
77

8-
from utils import tools
8+
from utils import tools, common
99
from algorithm.base import Algorithm
1010

1111

@@ -25,21 +25,19 @@ def set_params(self, solution, n_iter, **params):
2525
self.params = params
2626

2727

28-
def solve(self, verbose=False, k=2):
29-
self.cur_cost = tools.compute_solution(self.problem, self.solution)
28+
def solve(self, verbose=False, k=3):
29+
self.cur_cost = common.compute_solution(self.problem, self.solution)
3030
if verbose:
3131
print('Start cost: {}'.format(self.cur_cost))
3232
for comb in tqdm(combinations(np.arange(1, len(self.solution)-k,
33-
dtype=np.int32),
34-
2)
35-
):
33+
dtype=np.int32), 2)):
3634
tmp_sol = copy(self.solution)
3735
i, j = comb
38-
if abs(i-j) > 1:
36+
if abs(i-j) > k:
3937
tmp_sol[i:i+k], tmp_sol[j:j+k] = copy(tmp_sol[j:j+k]), copy(tmp_sol[i:i+k])
4038
else:
4139
continue
42-
cost = tools.compute_solution(self.problem, tmp_sol)
40+
cost = common.compute_solution(self.problem, tmp_sol)
4341
if self.cur_cost > cost:
4442
self.cur_cost = cost
4543
self.solution = tmp_sol
@@ -48,4 +46,5 @@ def solve(self, verbose=False, k=2):
4846
break
4947
self.n_iter -= 1
5048

49+
5150
return self.solution, self.cur_cost

‎algorithm/neighbor_operator.py‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import random
2+
import numpy as np
3+
from copy import copy
4+
from utils import tools
5+
from random import randint
6+
from itertools import combinations
7+
8+
9+
class NeighborOperator:
10+
11+
def __init__(self):
12+
operators =
13+
{0: self.random_swap}
14+
pass
15+
16+
@staticmethod
17+
def random_swap(_solution, patience = 100):
18+
solution = copy(_solution)
19+
sol_len = len(solution)
20+
while patience > 0:
21+
i, j = random.sample(np.arange(1, sol_len-1), 2)
22+
if i != j and solution[i] != 0 and solution[j] != 0:
23+
solution[i],
24+
solution[j] = copy(solution[j]),
25+
copy(solution[i])
26+
break
27+
patience -= 1
28+
return solution

‎utils/common.py‎

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import random
2+
import numpy as np
3+
import pandas as pd
4+
5+
from copy import copy
6+
from datetime import datetime
7+
from itertools import combinations
8+
from tqdm import tqdm, tqdm_notebook
9+
10+
11+
def generate_solution(problem,
12+
patience=10,
13+
verbose=False) -> np.ndarray:
14+
15+
dists = problem['dists']
16+
demands = problem['demands']
17+
i_loc = [i for i in range(1, problem['n_locations'])]
18+
routes = [[0] for _ in range(problem['n_trucks'])]
19+
20+
for i in range(len(i_loc)):
21+
route_dists = []
22+
random_loc = random.choice(i_loc)
23+
for route in routes:
24+
dist_to_loc = dists[route[-1]][random_loc]
25+
route_demand = sum([demands[i] for i in route])
26+
alpha = (route_demand + 0.5*dist_to_loc)
27+
route_dists.append(alpha)
28+
routes[np.argmin(route_dists)].append(random_loc)
29+
i_loc.remove(random_loc)
30+
31+
solution = [loc for route in routes for loc in route]
32+
solution.append(0)
33+
solution = np.array(solution, dtype=np.int32)
34+
35+
return solution
36+
37+
38+
def compute_solution(problem, solution) -> np.float32:
39+
# compute solution cost
40+
# cost = Sum DijXij
41+
n = problem['n_locations']
42+
x = np.zeros((n, n), dtype=np.int32)
43+
for i, loc in enumerate(solution[:-1]):
44+
x[solution[i], solution[i+1]] = 1
45+
cost = problem['dists'][x == 1].sum()
46+
return cost
47+
48+
49+
def check_solution(problem,
50+
solution,
51+
x=None,
52+
verbose=False) -> bool:
53+
# sanity check No1
54+
# len(solution) == n_locations + n_trucks
55+
sol_len = len(solution)
56+
plan_len = problem['n_trucks'] + problem['n_locations']
57+
if not sol_len == plan_len:
58+
if verbose:
59+
print('Solution len {} but should be {}' \
60+
.format(sol_len, plan_len))
61+
return False
62+
63+
# sanity check No2
64+
# The end and the start of the solution should be depot
65+
depots = list(filter(lambda i: solution[i]==0, range(sol_len)))
66+
if depots[0] != 0 or depots[-1] != sol_len-1:
67+
if verbose:
68+
print('The end and the start of the solution should be depots')
69+
print(depots)
70+
return False
71+
72+
# sanity check No3
73+
# there shouldn`t be several depots in a row for example [0, 0,.. ]
74+
for i in range(len(depots)-1):
75+
if depots[i+1] - depots[i] <= 1:
76+
if verbose:
77+
print('Several depots in a row: {}'.format(depots))
78+
return False
79+
80+
if not isinstance(x, np.ndarray):
81+
n = problem['n_locations']
82+
x = np.zeros((n, n), dtype=np.int32)
83+
for i, loc in enumerate(solution[:-1]):
84+
x[solution[i], solution[i+1]] = 1
85+
86+
# cruteria check No1
87+
# Sum Xi0 = M For all i in V
88+
# Sum X0j = M For all j in V and
89+
# where M is the number of trucks
90+
if not check_M_criteria(problem,
91+
solution,
92+
x=x,
93+
verbose=verbose):
94+
return False
95+
96+
# criteria check No2
97+
# Sum Xij = 1 For all j in V\{0} and
98+
# Sum Xij = 1 For all i in V\{0}
99+
if not check_One_criteria(problem,
100+
solution,
101+
x=x,
102+
verbose=verbose):
103+
return False
104+
105+
# criteria check No3
106+
# route demand <= truck capacity
107+
if not check_capacity_criteria(problem,
108+
solution,
109+
verbose=verbose):
110+
return False
111+
112+
return True
113+
114+
115+
def check_One_criteria(problem,
116+
solution,
117+
x=None,
118+
verbose=False) -> bool:
119+
if not ((x.sum(axis=1)[1: ].sum() == problem['n_locations'] - 1) and
120+
(x.sum(axis=0)[1: ].sum() == problem['n_locations'] - 1)):
121+
if verbose:
122+
print('Sum Xij for j = ', x.sum(axis=1)[1: ])
123+
print('Sum Xij for j = ', x.sum(axis=0)[1: ])
124+
return False
125+
126+
return True
127+
128+
129+
def check_M_criteria(problem,
130+
solution,
131+
x=None,
132+
verbose=False) -> bool:
133+
134+
if not isinstance(x, np.ndarray):
135+
n = problem['n_locations']
136+
x = np.zeros((n, n), dtype=np.int32)
137+
for i, loc in enumerate(solution[:-1]):
138+
x[solution[i], solution[i+1]] = 1
139+
140+
if not ((x[0, :].sum() == problem['n_trucks']) and
141+
x[:, 0].sum() == problem['n_trucks']):
142+
if verbose:
143+
print('n_trucks =', problem['n_trucks'])
144+
print('Sum Xi0 = ', x[:, 0].sum())
145+
print('Sum X0j = ', x[0, :].sum())
146+
print(solution)
147+
return False
148+
149+
return True
150+
151+
152+
def check_capacity_criteria(problem,
153+
solution,
154+
verbose=False) -> bool:
155+
capacity = problem['capacity']
156+
routes_demand = get_routes_demand(problem, solution)
157+
for route_demand in routes_demand:
158+
if route_demand > capacity:
159+
if verbose:
160+
print('Route demand {} exeeds capacity {}' \
161+
.format(route_demand, capacity))
162+
print('Route ', routes_demand)
163+
return False
164+
return True
165+
166+
167+
def get_routes_demand(problem, solution):
168+
sol_len = len(solution)
169+
demands = problem['demands']
170+
depots = list(filter(lambda i: solution[i]==0, range(sol_len)))
171+
routes = []
172+
for i, d in enumerate(depots[:-1]):
173+
route = solution[depots[i]+1:depots[i+1]]
174+
route_demand = np.sum([demands[place] for place in route])
175+
routes.append(route_demand)
176+
return routes

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /