randommm commited on
Commit
5d537f3
·
1 Parent(s): 0dce8af
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. facility_location/__pycache__/__init__.cpython-310.pyc +0 -0
  2. facility_location/__pycache__/__init__.cpython-37.pyc +0 -0
  3. facility_location/__pycache__/__init__.cpython-39.pyc +0 -0
  4. facility_location/__pycache__/eval.cpython-310.pyc +0 -0
  5. facility_location/__pycache__/eval.cpython-39.pyc +0 -0
  6. facility_location/__pycache__/multi_eval.cpython-310.pyc +0 -0
  7. facility_location/__pycache__/multi_eval.cpython-39.pyc +0 -0
  8. facility_location/__pycache__/train.cpython-310.pyc +0 -0
  9. facility_location/__pycache__/train.cpython-37.pyc +0 -0
  10. facility_location/__pycache__/train.cpython-39.pyc +0 -0
  11. facility_location/agent/__pycache__/__init__.cpython-39.pyc +0 -0
  12. facility_location/agent/__pycache__/features_extractor.cpython-39.pyc +0 -0
  13. facility_location/agent/__pycache__/policy.cpython-39.pyc +0 -0
  14. facility_location/agent/__pycache__/solver.cpython-39.pyc +0 -0
  15. facility_location/agent/ga.py +0 -86
  16. facility_location/agent/heuristic.py +0 -72
  17. facility_location/agent/metaheuristic.py +0 -218
  18. facility_location/agent/tests/ga.ipynb +0 -0
  19. facility_location/agent/tests/solver.ipynb +0 -142
  20. facility_location/cfg/2-nearest.yaml +0 -61
  21. facility_location/cfg/3-nearest.yaml +0 -63
  22. facility_location/cfg/NY.yaml +0 -65
  23. facility_location/cfg/dg.yaml +0 -63
  24. facility_location/cfg/gainloss.yaml +0 -63
  25. facility_location/cfg/multi.yaml +0 -69
  26. facility_location/cfg/plot.yaml +4 -4
  27. facility_location/cfg/popstar.yaml +0 -63
  28. facility_location/cfg/scale1.yaml +0 -63
  29. facility_location/cfg/scale5.yaml +0 -63
  30. facility_location/cfg/tabu0.yaml +0 -63
  31. facility_location/cfg/tabu5.yaml +0 -63
  32. facility_location/cfg/uniform.yaml +0 -63
  33. facility_location/cfg/uniform_debug.yaml +0 -64
  34. facility_location/env/__pycache__/__init__.cpython-39.pyc +0 -0
  35. facility_location/env/__pycache__/facility_location_client.cpython-310.pyc +0 -0
  36. facility_location/env/__pycache__/facility_location_client.cpython-39.pyc +0 -0
  37. facility_location/env/__pycache__/obs_extractor.cpython-310.pyc +0 -0
  38. facility_location/env/__pycache__/obs_extractor.cpython-39.pyc +0 -0
  39. facility_location/env/__pycache__/pmp.cpython-310.pyc +0 -0
  40. facility_location/env/__pycache__/pmp.cpython-39.pyc +0 -0
  41. facility_location/env/facility_location_client.py +44 -51
  42. facility_location/env/obs_extractor.py +1 -19
  43. facility_location/env/tests/p-median.ipynb +0 -0
  44. facility_location/env/tests/render.ipynb +0 -0
  45. facility_location/env/utils/env_test.ipynb +0 -0
  46. facility_location/eval.py +0 -234
  47. facility_location/multi_eval.py +15 -126
  48. facility_location/solutions.pkl +3 -0
  49. facility_location/test.ipynb +0 -425
  50. facility_location/train.py +0 -274
facility_location/__pycache__/__init__.cpython-310.pyc DELETED
Binary file (144 Bytes)
 
facility_location/__pycache__/__init__.cpython-37.pyc DELETED
Binary file (138 Bytes)
 
facility_location/__pycache__/__init__.cpython-39.pyc CHANGED
Binary files a/facility_location/__pycache__/__init__.cpython-39.pyc and b/facility_location/__pycache__/__init__.cpython-39.pyc differ
 
facility_location/__pycache__/eval.cpython-310.pyc DELETED
Binary file (6.27 kB)
 
facility_location/__pycache__/eval.cpython-39.pyc DELETED
Binary file (5.23 kB)
 
facility_location/__pycache__/multi_eval.cpython-310.pyc DELETED
Binary file (5.57 kB)
 
facility_location/__pycache__/multi_eval.cpython-39.pyc ADDED
Binary file (3.13 kB). View file
 
facility_location/__pycache__/train.cpython-310.pyc DELETED
Binary file (8.45 kB)
 
facility_location/__pycache__/train.cpython-37.pyc DELETED
Binary file (7.06 kB)
 
facility_location/__pycache__/train.cpython-39.pyc DELETED
Binary file (7.71 kB)
 
facility_location/agent/__pycache__/__init__.cpython-39.pyc CHANGED
Binary files a/facility_location/agent/__pycache__/__init__.cpython-39.pyc and b/facility_location/agent/__pycache__/__init__.cpython-39.pyc differ
 
facility_location/agent/__pycache__/features_extractor.cpython-39.pyc CHANGED
Binary files a/facility_location/agent/__pycache__/features_extractor.cpython-39.pyc and b/facility_location/agent/__pycache__/features_extractor.cpython-39.pyc differ
 
facility_location/agent/__pycache__/policy.cpython-39.pyc CHANGED
Binary files a/facility_location/agent/__pycache__/policy.cpython-39.pyc and b/facility_location/agent/__pycache__/policy.cpython-39.pyc differ
 
facility_location/agent/__pycache__/solver.cpython-39.pyc CHANGED
Binary files a/facility_location/agent/__pycache__/solver.cpython-39.pyc and b/facility_location/agent/__pycache__/solver.cpython-39.pyc differ
 
facility_location/agent/ga.py DELETED
@@ -1,86 +0,0 @@
1
- import numpy as np
2
- import pygad
3
-
4
- from facility_location.env import EvalPMPEnv
5
- from facility_location.utils import Config
6
-
7
-
8
- class PMPGA:
9
- def __init__(self, cfg: Config, env: EvalPMPEnv):
10
- ga_specs = cfg.ga_specs
11
- self._num_generations = ga_specs['num_generations']
12
- self._num_parents_mating = ga_specs['num_parents_mating']
13
- self._sol_per_pop = ga_specs['sol_per_pop']
14
- self._parent_selection_type = ga_specs['parent_selection_type']
15
- self._crossover_probability = ga_specs['crossover_probability']
16
- self._mutation_probability = ga_specs['mutation_probability']
17
-
18
- self.env = env
19
- self.seed = cfg.seed
20
- self._np_random = np.random.default_rng(cfg.seed)
21
-
22
- def solve(self) -> np.ndarray:
23
- _, _, n, p = self.env.get_instance()
24
-
25
- def fitness_func(solution: np.ndarray, solution_idx: int) -> float:
26
- solution = solution.astype(bool)
27
- reward = self.env.evaluate(solution)
28
- fitness = -reward
29
- return fitness
30
-
31
- def crossover_func(parents, offspring_size, ga_instance):
32
- offsprings = []
33
- idx = 0
34
- while len(offsprings) != offspring_size[0]:
35
- offspring = np.zeros(n, dtype=np.int32)
36
-
37
- parent1 = parents[idx % parents.shape[0], :].copy()
38
- parent2 = parents[(idx + 1) % parents.shape[0], :].copy()
39
- facility_locations = np.arange(n)[(parent1 + parent2) > 0]
40
- random_indices = self._np_random.choice(facility_locations, p, replace=False)
41
- offspring[random_indices] = 1
42
- offsprings.append(offspring)
43
-
44
- idx += 1
45
-
46
- return np.array(offsprings)
47
-
48
- def mutation_func(offsprings, ga_instance):
49
-
50
- for offspring_idx in range(offsprings.shape[0]):
51
- offspring = offsprings[offspring_idx]
52
- facility_locations = np.arange(n)[offspring == 1]
53
- vacant_locations = np.arange(n)[offspring == 0]
54
- old_facility_location = self._np_random.choice(facility_locations)
55
- new_facility_location = self._np_random.choice(vacant_locations)
56
-
57
- offsprings[offspring_idx, old_facility_location] = 0
58
- offsprings[offspring_idx, new_facility_location] = 1
59
-
60
- return offsprings
61
-
62
- initial_population = self._generate_initial_population(n, p)
63
- ga_instance = pygad.GA(num_generations=self._num_generations,
64
- num_parents_mating=self._num_parents_mating,
65
- fitness_func=fitness_func,
66
- initial_population=initial_population,
67
- sol_per_pop=self._sol_per_pop,
68
- gene_type=np.int32,
69
- parent_selection_type=self._parent_selection_type,
70
- crossover_type=crossover_func,
71
- crossover_probability=self._crossover_probability,
72
- mutation_type=mutation_func,
73
- mutation_probability=self._mutation_probability,
74
- stop_criteria="saturate_20",
75
- random_seed=self.seed)
76
- ga_instance.run()
77
- best_solution, _, _ = ga_instance.best_solution()
78
- best_solution = best_solution.astype(bool)
79
- return best_solution
80
-
81
- def _generate_initial_population(self, n: int, p: int) -> np.ndarray:
82
- initial_population = np.zeros((self._sol_per_pop, n), dtype=np.int32)
83
- for i in range(self._sol_per_pop):
84
- random_indices = self._np_random.choice(n, p, replace=False)
85
- initial_population[i, random_indices] = 1
86
- return initial_population
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
facility_location/agent/heuristic.py DELETED
@@ -1,72 +0,0 @@
1
- import subprocess
2
- import tempfile
3
-
4
- import numpy as np
5
-
6
- from facility_location.env import EvalPMPEnv
7
-
8
-
9
- class HeuristicRandom:
10
- def __init__(self, seed: int, env: EvalPMPEnv):
11
- self._np_random = np.random.default_rng(seed)
12
-
13
- self.env = env
14
-
15
- def solve(self):
16
- _, _, n, p = self.env.get_instance()
17
- solution = np.zeros(n, dtype=bool)
18
- solution[self._np_random.choice(n, p, replace=False)] = True
19
- return solution
20
-
21
-
22
- class HeuristicGreedy:
23
- def __init__(self, env: EvalPMPEnv):
24
- self.env = env
25
-
26
- def solve(self):
27
- solution = self.env.get_initial_solution()
28
- return solution
29
-
30
-
31
- class HeuristicFastInterchange:
32
- def __init__(self, env: EvalPMPEnv):
33
- self.env = env
34
-
35
- def solve(self):
36
- temp_input_file = tempfile.NamedTemporaryFile(mode='w', suffix='.pmm')
37
- temp_initsol_file = tempfile.NamedTemporaryFile(mode='w')
38
- temp_output_file = tempfile.NamedTemporaryFile(mode='r')
39
- _, _, n, p = self.env.get_instance()
40
- _, cost_matrix = self.env.get_distance_and_cost()
41
- initial_solution = self.env.get_initial_solution()
42
- initial_solution = np.where(initial_solution)[0] + 1
43
- label_initial_solution = np.column_stack([np.zeros(len(initial_solution)), initial_solution])
44
- i, j = np.indices(cost_matrix.shape)
45
- triplets = np.column_stack([ar.ravel() for ar in (i+1, j+1, cost_matrix)])
46
- label_triplets = np.column_stack([np.zeros(len(triplets)), triplets])
47
- try:
48
- np.savetxt(temp_input_file.name, label_triplets, fmt='%d %d %d %.8f',
49
- delimiter=' ',
50
- header=f'p {n} {n}',
51
- comments='')
52
- np.savetxt(temp_initsol_file.name, label_initial_solution, fmt='%d %d', delimiter=' ')
53
- subprocess.run(["thirdparty/popstar/popstar", temp_input_file.name,
54
- "-p", f"{p}",
55
- "-output", temp_output_file.name,
56
- "-nograsp",
57
- "-run_ls",
58
- "-inputsol", temp_initsol_file.name,
59
- "-ch", "rgreedy:1",
60
- "-elite", "0"],
61
- stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
62
- fi_solution = np.loadtxt(temp_output_file.name, skiprows=4, max_rows=p,
63
- dtype={'names': ('facility', 'index'),
64
- 'formats': ('S1', 'i4')})
65
- solution = np.full(n, False)
66
- solution[fi_solution['index'] - 1] = True
67
- finally:
68
- temp_input_file.close()
69
- temp_initsol_file.close()
70
- temp_output_file.close()
71
-
72
- return solution
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
facility_location/agent/metaheuristic.py DELETED
@@ -1,218 +0,0 @@
1
- import random
2
- import subprocess
3
- import tempfile
4
-
5
- import numpy as np
6
-
7
- from facility_location.env import EvalPMPEnv
8
- from facility_location.utils import Config
9
-
10
-
11
- class TabuSearch:
12
- def __init__(self, cfg: Config, env: EvalPMPEnv):
13
- ts_specs = cfg.ts_specs
14
- self.max_steps_scale = ts_specs['max_steps_scale']
15
- self.stable_iterations_scale = ts_specs['stable_iterations_scale']
16
-
17
- self.env = env
18
-
19
- def init_variables(self, n: int, p: int):
20
- self.max_iterations = max(self.max_steps_scale * n, 100)
21
- self.stable_iterations = round(self.stable_iterations_scale * self.max_iterations)
22
- self.iteration = 0
23
- self.best_value = np.inf
24
- self.slack = 0
25
- self.add_time = np.full(n, -np.inf)
26
- self.freq = np.zeros(n)
27
- self.S = np.full(n, False)
28
- self.NS = np.full(n, True)
29
- self.k = self.distances.max()
30
- self.last_improvement = self.iteration
31
- self.tabu_time = random.randint(1, p + 1)
32
-
33
- def solve(self):
34
- _, self.demands, self.n, self.p = self.env.get_instance()
35
- self.distances, self.cost_matrix = self.env.get_distance_and_cost()
36
- self.init_variables(self.n, self.p)
37
- _, solution = self.run()
38
- return solution
39
-
40
- def run(self):
41
- while np.count_nonzero(self.S) < self.p:
42
- new_value = self.add()
43
- self.best_value = new_value
44
-
45
- while self.iteration < self.max_iterations:
46
- new_value = self.choose_move()
47
- self.iteration += 1
48
-
49
- if np.count_nonzero(self.S) == self.p and new_value < self.best_value:
50
- self.best_value = new_value
51
- self.slack = 0
52
- self.last_improvement = self.iteration
53
- else:
54
- iteration_since_last_improvement = self.iteration - self.last_improvement
55
- if iteration_since_last_improvement % (self.stable_iterations * 2) == 0:
56
- self.slack += 1
57
- if iteration_since_last_improvement % round(self.stable_iterations / 2) == 0:
58
- self.tabu_time = random.randint(1, self.p + 1)
59
- if np.count_nonzero(self.S) == self.p and iteration_since_last_improvement >= self.stable_iterations:
60
- self.iteration = self.max_iterations
61
-
62
- return self.best_value, self.S
63
-
64
- def evaluate(self, v_candidate, m_type):
65
- if m_type == 'ADD':
66
- self.S[v_candidate] = True
67
- self.NS[v_candidate] = False
68
- else:
69
- self.S[v_candidate] = False
70
- self.NS[v_candidate] = True
71
-
72
- cost = self.env.evaluate(self.S)
73
- if m_type == 'ADD':
74
- v_candidate_index_in_S = np.where(np.arange(self.n)[self.S] == v_candidate)[0][0]
75
- assigned_customers = self.cost_matrix[:, self.S].argmin(axis=-1) == v_candidate_index_in_S
76
- penalty = self.k * self.freq[v_candidate] * self.demands[assigned_customers].sum()
77
- cost += penalty
78
-
79
- if m_type == 'ADD':
80
- self.S[v_candidate] = False
81
- self.NS[v_candidate] = True
82
- else:
83
- self.S[v_candidate] = True
84
- self.NS[v_candidate] = False
85
-
86
- return cost
87
-
88
- def is_tabu(self, v):
89
- return self.add_time[v] >= self.iteration - self.tabu_time
90
-
91
- def flip_coin(self):
92
- return random.random() < 0.5
93
-
94
- def add(self):
95
- new_value = np.inf
96
- best_candidate = -1
97
- candidates = np.where(self.NS)[0]
98
- for v in candidates:
99
- if self.is_tabu(v):
100
- continue
101
- value = self.evaluate(v, 'ADD')
102
- if value < new_value:
103
- new_value = value
104
- best_candidate = v
105
-
106
- if best_candidate >= 0:
107
- self.add_time[best_candidate] = self.iteration
108
- self.S[best_candidate] = True
109
- self.NS[best_candidate] = False
110
- self.freq[best_candidate] += 1
111
-
112
- return new_value
113
-
114
- def aspiration_criteria(self, value):
115
- return value < self.best_value
116
-
117
- def drop(self):
118
- new_value = np.inf
119
- best_candidate = -1
120
- candidates = np.where(self.S)[0]
121
- for v in candidates:
122
- value = self.evaluate(v, 'DROP')
123
- if (not self.is_tabu(v) or self.aspiration_criteria(value)) and value < new_value:
124
- new_value = value
125
- best_candidate = v
126
-
127
- if best_candidate >= 0:
128
- self.NS[best_candidate] = True
129
- self.S[best_candidate] = False
130
-
131
- return new_value
132
-
133
- def choose_move(self):
134
- if np.count_nonzero(self.S) < self.p - self.slack:
135
- return self.add()
136
- elif np.count_nonzero(self.S) > self.p + self.slack:
137
- return self.drop()
138
- elif self.flip_coin() and np.count_nonzero(self.S) > 0:
139
- return self.drop()
140
- else:
141
- return self.add()
142
-
143
-
144
- class VNS:
145
- def __init__(self, env: EvalPMPEnv):
146
- self.env = env
147
-
148
- def solve(self):
149
- temp_input_file = tempfile.NamedTemporaryFile(mode='w', suffix='.pmm')
150
- temp_output_file = tempfile.NamedTemporaryFile(mode='r')
151
- _, _, n, p = self.env.get_instance()
152
- _, cost_matrix = self.env.get_distance_and_cost()
153
- i, j = np.indices(cost_matrix.shape)
154
- triplets = np.column_stack([ar.ravel() for ar in (i+1, j+1, cost_matrix)])
155
- label_triplets = np.column_stack([np.zeros(len(triplets)), triplets])
156
- try:
157
- np.savetxt(temp_input_file.name, label_triplets, fmt='%d %d %d %.8f',
158
- delimiter=' ',
159
- header=f'p {n} {n}',
160
- comments='')
161
- subprocess.run(["thirdparty/popstar/popstar", temp_input_file.name,
162
- "-p", f"{p}",
163
- "-output", temp_output_file.name,
164
- "-nograsp",
165
- "-run_vns",
166
- "-ch", "rgreedy:1",
167
- "-elite", "0"],
168
- stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
169
- vns_solution = np.loadtxt(temp_output_file.name, skiprows=4, max_rows=p,
170
- dtype={'names': ('facility', 'index'),
171
- 'formats': ('S1', 'i4')})
172
- solution = np.full(n, False)
173
- solution[vns_solution['index'] - 1] = True
174
- finally:
175
- temp_input_file.close()
176
- temp_output_file.close()
177
-
178
- return solution
179
-
180
-
181
- class POPSTAR:
182
- def __init__(self, cfg: Config, env: EvalPMPEnv):
183
- popstar_specs = cfg.popstar_specs
184
- self.graspit = popstar_specs['graspit']
185
- self.elite = popstar_specs['elite']
186
-
187
- self.env = env
188
-
189
- def solve(self):
190
- temp_input_file = tempfile.NamedTemporaryFile(mode='w', suffix='.pmm')
191
- temp_output_file = tempfile.NamedTemporaryFile(mode='r')
192
- _, _, n, p = self.env.get_instance()
193
- _, cost_matrix = self.env.get_distance_and_cost()
194
- i, j = np.indices(cost_matrix.shape)
195
- triplets = np.column_stack([ar.ravel() for ar in (i+1, j+1, cost_matrix)])
196
- label_triplets = np.column_stack([np.zeros(len(triplets)), triplets])
197
- try:
198
- np.savetxt(temp_input_file.name, label_triplets, fmt='%d %d %d %.8f',
199
- delimiter=' ',
200
- header=f'p {n} {n}',
201
- comments='')
202
- subprocess.run(["thirdparty/popstar/popstar", temp_input_file.name,
203
- "-p", f"{p}",
204
- "-output", temp_output_file.name,
205
- "-graspit", f"{self.graspit}",
206
- "-elite", f"{self.elite}"],
207
- stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
208
- popstar_solution = np.loadtxt(temp_output_file.name, skiprows=4, max_rows=p,
209
- dtype={'names': ('facility', 'index'),
210
- 'formats': ('S1', 'i4')})
211
- solution = np.full(n, False)
212
- solution[popstar_solution['index'] - 1] = True
213
- finally:
214
- temp_input_file.close()
215
- temp_output_file.close()
216
-
217
- return solution
218
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
facility_location/agent/tests/ga.ipynb DELETED
The diff for this file is too large to render. See raw diff
 
facility_location/agent/tests/solver.ipynb DELETED
@@ -1,142 +0,0 @@
1
- {
2
- "cells": [
3
- {
4
- "cell_type": "code",
5
- "execution_count": 1,
6
- "id": "5880eb74",
7
- "metadata": {},
8
- "outputs": [],
9
- "source": [
10
- "import numpy as np\n",
11
- "from sklearn.metrics import pairwise_distances\n",
12
- "import time\n",
13
- "from tqdm import tqdm\n",
14
- "\n",
15
- "from spopt.locate import PMedian\n",
16
- "import pulp"
17
- ]
18
- },
19
- {
20
- "cell_type": "code",
21
- "execution_count": 2,
22
- "id": "abaedea7",
23
- "metadata": {},
24
- "outputs": [],
25
- "source": [
26
- "rng = np.random.default_rng()"
27
- ]
28
- },
29
- {
30
- "cell_type": "code",
31
- "execution_count": 3,
32
- "id": "569623ca",
33
- "metadata": {},
34
- "outputs": [],
35
- "source": [
36
- "def pulp_solve(points, demands, p, solver):\n",
37
- " distance_matrix = pairwise_distances(points)\n",
38
- " cost_matrix = distance_matrix * demands[:, None]\n",
39
- " pmedian_from_cost_matrix = PMedian.from_cost_matrix(cost_matrix, demands, p_facilities=p)\n",
40
- " pmedian_from_cost_matrix = pmedian_from_cost_matrix.solve(solver)\n",
41
- " return np.array([len(temp) > 0 for temp in pmedian_from_cost_matrix.fac2cli], dtype=bool)"
42
- ]
43
- },
44
- {
45
- "cell_type": "code",
46
- "execution_count": 11,
47
- "id": "a67e61dc",
48
- "metadata": {},
49
- "outputs": [
50
- {
51
- "name": "stderr",
52
- "output_type": "stream",
53
- "text": [
54
- "100%|██████████| 2/2 [00:19<00:00, 9.79s/it]"
55
- ]
56
- },
57
- {
58
- "name": "stdout",
59
- "output_type": "stream",
60
- "text": [
61
- "time: 9.795565128326416\n"
62
- ]
63
- },
64
- {
65
- "name": "stderr",
66
- "output_type": "stream",
67
- "text": [
68
- "\n"
69
- ]
70
- }
71
- ],
72
- "source": [
73
- "solver = pulp.PULP_CBC_CMD(msg=False)\n",
74
- "solver = pulp.GLPK_CMD(msg=False)\n",
75
- "solver = pulp.GUROBI(msg=False)\n",
76
- "#solver = pulp.GUROBI_CMD(msg=False)\n",
77
- "n = 200\n",
78
- "p = 4\n",
79
- "num_exp = 2\n",
80
- "all_points = rng.uniform(size=(num_exp, n, 2))\n",
81
- "all_demands = rng.random(size=(num_exp, n))\n",
82
- "start_time = time.time()\n",
83
- "for idx in tqdm(range(num_exp)):\n",
84
- " points = all_points[idx]\n",
85
- " demands = all_demands[idx]\n",
86
- " solution = pulp_solve(points, demands, p, solver)\n",
87
- "print(f'time: {(time.time() - start_time)/num_exp}')"
88
- ]
89
- },
90
- {
91
- "cell_type": "code",
92
- "execution_count": 8,
93
- "id": "679b6f4b",
94
- "metadata": {},
95
- "outputs": [
96
- {
97
- "name": "stdout",
98
- "output_type": "stream",
99
- "text": [
100
- "solvers: ['GLPK_CMD', 'PYGLPK', 'CPLEX_CMD', 'CPLEX_PY', 'GUROBI', 'GUROBI_CMD', 'MOSEK', 'XPRESS', 'XPRESS', 'XPRESS_PY', 'PULP_CBC_CMD', 'COIN_CMD', 'COINMP_DLL', 'CHOCO_CMD', 'MIPCL_CMD', 'SCIP_CMD', 'HiGHS_CMD']\n",
101
- "available solvers: ['GLPK_CMD', 'GUROBI', 'GUROBI_CMD', 'PULP_CBC_CMD']\n"
102
- ]
103
- }
104
- ],
105
- "source": [
106
- "solver_list = pulp.listSolvers()\n",
107
- "available_solver_list = pulp.listSolvers(onlyAvailable=True)\n",
108
- "print(f'solvers: {solver_list}')\n",
109
- "print(f'available solvers: {available_solver_list}')"
110
- ]
111
- },
112
- {
113
- "cell_type": "code",
114
- "execution_count": null,
115
- "id": "143a6eb9",
116
- "metadata": {},
117
- "outputs": [],
118
- "source": []
119
- }
120
- ],
121
- "metadata": {
122
- "kernelspec": {
123
- "display_name": "Python 3",
124
- "language": "python",
125
- "name": "python3"
126
- },
127
- "language_info": {
128
- "codemirror_mode": {
129
- "name": "ipython",
130
- "version": 3
131
- },
132
- "file_extension": ".py",
133
- "mimetype": "text/x-python",
134
- "name": "python",
135
- "nbconvert_exporter": "python",
136
- "pygments_lexer": "ipython3",
137
- "version": "3.9.7"
138
- }
139
- },
140
- "nbformat": 4,
141
- "nbformat_minor": 5
142
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
facility_location/cfg/2-nearest.yaml DELETED
@@ -1,61 +0,0 @@
1
- # env
2
- env_specs:
3
- min_n: 20
4
- max_n: 50
5
- min_p_ratio: 0.1
6
- max_p_ratio: 0.4
7
- max_steps_scale: 1
8
- tabu_time: 3
9
- tabu_stable_steps_scale: 0.1
10
- popstar: false
11
-
12
- # evaluation
13
- eval_specs:
14
- seed: 12345
15
- val_num_cases: 100
16
- test_num_cases: 100
17
- val_np: !!python/tuple [50, 10]
18
- test_np:
19
- - !!python/tuple [50, 5]
20
- - !!python/tuple [100, 10]
21
- - !!python/tuple [400, 50]
22
-
23
- # agent
24
- agent_specs:
25
- policy_feature_dim: 32
26
- value_feature_dim: 32
27
- policy_hidden_units: !!python/tuple [32, 32, 1]
28
- value_hidden_units: !!python/tuple [32, 32, 1]
29
-
30
- # mlp
31
- mlp_specs:
32
- hidden_units: !!python/tuple [32, 32]
33
-
34
- gnn_specs:
35
- num_gnn_layers: 2
36
- node_dim: 32
37
-
38
-
39
- # ts
40
- ts_specs:
41
- max_steps_scale: 2
42
- stable_iterations_scale: 0.2
43
-
44
-
45
- # popstar
46
- popstar_specs:
47
- graspit: 32
48
- elite: 10
49
-
50
-
51
- # ga
52
- ga_specs:
53
- num_generations: 100
54
- num_parents_mating: 50
55
- sol_per_pop: 100
56
- parent_selection_type: sss
57
- crossover_probability: 0.8
58
- mutation_probability: 0.1
59
-
60
-
61
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
facility_location/cfg/3-nearest.yaml DELETED
@@ -1,63 +0,0 @@
1
- # env
2
- env_specs:
3
- region:
4
- min_n: 20
5
- max_n: 50
6
- min_p_ratio: 0.1
7
- max_p_ratio: 0.4
8
- max_steps_scale: 3
9
- tabu_time: 3
10
- tabu_stable_steps_scale: 0.1
11
- popstar: false
12
-
13
- # evaluation
14
- eval_specs:
15
- region:
16
- seed: 12345
17
- val_num_cases: 100
18
- test_num_cases: 100
19
- val_np: !!python/tuple [50, 10]
20
- test_np:
21
- - !!python/tuple [50, 5]
22
- - !!python/tuple [100, 10]
23
- - !!python/tuple [400, 50]
24
-
25
- # agent
26
- agent_specs:
27
- policy_feature_dim: 32
28
- value_feature_dim: 32
29
- policy_hidden_units: !!python/tuple [32, 32, 1]
30
- value_hidden_units: !!python/tuple [32, 32, 1]
31
-
32
- # mlp
33
- mlp_specs:
34
- hidden_units: !!python/tuple [32, 32]
35
-
36
- gnn_specs:
37
- num_gnn_layers: 2
38
- node_dim: 32
39
-
40
-
41
- # ts
42
- ts_specs:
43
- max_steps_scale: 2
44
- stable_iterations_scale: 0.2
45
-
46
-
47
- # popstar
48
- popstar_specs:
49
- graspit: 32
50
- elite: 10
51
-
52
-
53
- # ga
54
- ga_specs:
55
- num_generations: 100
56
- num_parents_mating: 50
57
- sol_per_pop: 100
58
- parent_selection_type: sss
59
- crossover_probability: 0.8
60
- mutation_probability: 0.1
61
-
62
-
63
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
facility_location/cfg/NY.yaml DELETED
@@ -1,65 +0,0 @@
1
- # env
2
- env_specs:
3
- region: NY
4
- min_n: 50
5
- max_n: 299
6
- min_p_ratio: 0.05
7
- max_p_ratio: 0.0936455
8
- max_steps_scale: 3
9
- tabu_time: 2
10
- tabu_stable_steps_scale: 0.2
11
- popstar: false
12
-
13
- # evaluation
14
- eval_specs:
15
- region: NY
16
- seed: 12345
17
- max_nodes: 2488
18
- max_edges: 5000
19
- val_num_cases: 1
20
- test_num_cases: 1
21
- val_np: !!python/tuple [299, 28]
22
- test_np:
23
- - !!python/tuple [50, 5]
24
- - !!python/tuple [100, 10]
25
- - !!python/tuple [400, 50]
26
-
27
- # agent
28
- agent_specs:
29
- policy_feature_dim: 32
30
- value_feature_dim: 32
31
- policy_hidden_units: !!python/tuple [32, 32, 1]
32
- value_hidden_units: !!python/tuple [32, 32, 1]
33
-
34
- # mlp
35
- mlp_specs:
36
- hidden_units: !!python/tuple [32, 32]
37
-
38
- gnn_specs:
39
- num_gnn_layers: 2
40
- node_dim: 32
41
-
42
-
43
- # ts
44
- ts_specs:
45
- max_steps_scale: 2
46
- stable_iterations_scale: 0.2
47
-
48
-
49
- # popstar
50
- popstar_specs:
51
- graspit: 32
52
- elite: 10
53
-
54
-
55
- # ga
56
- ga_specs:
57
- num_generations: 100
58
- num_parents_mating: 50
59
- sol_per_pop: 100
60
- parent_selection_type: sss
61
- crossover_probability: 0.8
62
- mutation_probability: 0.1
63
-
64
-
65
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
facility_location/cfg/dg.yaml DELETED
@@ -1,63 +0,0 @@
1
- # env
2
- env_specs:
3
- region:
4
- min_n: 20
5
- max_n: 50
6
- min_p_ratio: 0.1
7
- max_p_ratio: 0.4
8
- max_steps_scale: 0.1
9
- tabu_time: 1
10
- tabu_stable_steps_scale: 0.2
11
- popstar: false
12
-
13
- # evaluation
14
- eval_specs:
15
- region: BO
16
- seed: 12345
17
- val_num_cases: 100
18
- test_num_cases: 100
19
- val_np: !!python/tuple [50,5]
20
- test_np:
21
- - !!python/tuple [50, 5]
22
- - !!python/tuple [100, 10]
23
- - !!python/tuple [400, 50]
24
-
25
- # agent
26
- agent_specs:
27
- policy_feature_dim: 32
28
- value_feature_dim: 32
29
- policy_hidden_units: !!python/tuple [32, 32, 1]
30
- value_hidden_units: !!python/tuple [32, 32, 1]
31
-
32
- # mlp
33
- mlp_specs:
34
- hidden_units: !!python/tuple [32, 32]
35
-
36
- gnn_specs:
37
- num_gnn_layers: 2
38
- node_dim: 32
39
-
40
-
41
- # ts
42
- ts_specs:
43
- max_steps_scale: 2
44
- stable_iterations_scale: 0.2
45
-
46
-
47
- # popstar
48
- popstar_specs:
49
- graspit: 32
50
- elite: 10
51
-
52
-
53
- # ga
54
- ga_specs:
55
- num_generations: 100
56
- num_parents_mating: 50
57
- sol_per_pop: 100
58
- parent_selection_type: sss
59
- crossover_probability: 0.8
60
- mutation_probability: 0.1
61
-
62
-
63
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
facility_location/cfg/gainloss.yaml DELETED
@@ -1,63 +0,0 @@
1
- # env
2
- env_specs:
3
- region:
4
- min_n: 20
5
- max_n: 50
6
- min_p_ratio: 0.1
7
- max_p_ratio: 0.4
8
- max_steps_scale: 3
9
- tabu_time: 3
10
- tabu_stable_steps_scale: 0.1
11
- popstar: false
12
-
13
- # evaluation
14
- eval_specs:
15
- region:
16
- seed: 12345
17
- val_num_cases: 100
18
- test_num_cases: 100
19
- val_np: !!python/tuple [50, 10]
20
- test_np:
21
- - !!python/tuple [50, 5]
22
- - !!python/tuple [100, 10]
23
- - !!python/tuple [400, 50]
24
-
25
- # agent
26
- agent_specs:
27
- policy_feature_dim: 32
28
- value_feature_dim: 32
29
- policy_hidden_units: !!python/tuple [32, 32, 1]
30
- value_hidden_units: !!python/tuple [32, 32, 1]
31
-
32
- # mlp
33
- mlp_specs:
34
- hidden_units: !!python/tuple [32, 32]
35
-
36
- gnn_specs:
37
- num_gnn_layers: 2
38
- node_dim: 32
39
-
40
-
41
- # ts
42
- ts_specs:
43
- max_steps_scale: 2
44
- stable_iterations_scale: 0.2
45
-
46
-
47
- # popstar
48
- popstar_specs:
49
- graspit: 32
50
- elite: 10
51
-
52
-
53
- # ga
54
- ga_specs:
55
- num_generations: 100
56
- num_parents_mating: 50
57
- sol_per_pop: 100
58
- parent_selection_type: sss
59
- crossover_probability: 0.8
60
- mutation_probability: 0.1
61
-
62
-
63
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
facility_location/cfg/multi.yaml DELETED
@@ -1,69 +0,0 @@
1
- # env
2
- env_specs:
3
- region:
4
- min_n: 20
5
- max_n: 50
6
- min_p_ratio: 0.1
7
- max_p_ratio: 0.4
8
- max_steps_scale: 3
9
- tabu_time: 3
10
- tabu_stable_steps_scale: 0.1
11
- popstar: false
12
-
13
- multi:
14
- nps: [(100,10),(100,20),(100,30)]
15
- number: True
16
- conflict: False
17
-
18
- # evaluation
19
- eval_specs:
20
- region:
21
- seed: 12345
22
- val_num_cases: 100
23
- test_num_cases: 100
24
- val_np: !!python/tuple [50, 10]
25
- test_np:
26
- - !!python/tuple [50, 5]
27
- - !!python/tuple [100, 10]
28
- - !!python/tuple [400, 50]
29
-
30
-
31
- # agent
32
- agent_specs:
33
- policy_feature_dim: 32
34
- value_feature_dim: 32
35
- policy_hidden_units: !!python/tuple [32, 32, 1]
36
- value_hidden_units: !!python/tuple [32, 32, 1]
37
-
38
- # mlp
39
- mlp_specs:
40
- hidden_units: !!python/tuple [32, 32]
41
-
42
- gnn_specs:
43
- num_gnn_layers: 2
44
- node_dim: 32
45
-
46
-
47
- # ts
48
- ts_specs:
49
- max_steps_scale: 2
50
- stable_iterations_scale: 0.2
51
-
52
-
53
- # popstar
54
- popstar_specs:
55
- graspit: 32
56
- elite: 10
57
-
58
-
59
- # ga
60
- ga_specs:
61
- num_generations: 100
62
- num_parents_mating: 50
63
- sol_per_pop: 100
64
- parent_selection_type: sss
65
- crossover_probability: 0.8
66
- mutation_probability: 0.1
67
-
68
-
69
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
facility_location/cfg/plot.yaml CHANGED
@@ -1,18 +1,18 @@
1
- # env
2
  env_specs:
3
  region:
4
  min_n: 20
5
  max_n: 50
6
  min_p_ratio: 0.1
7
  max_p_ratio: 0.4
8
- max_steps_scale: 3
9
- tabu_time: 1
10
  tabu_stable_steps_scale: 0.2
11
  popstar: false
12
 
13
  # evaluation
14
  eval_specs:
15
- region: test
16
  seed: 12345
17
  max_nodes: 2488
18
  max_edges: 5000
 
1
+
2
  env_specs:
3
  region:
4
  min_n: 20
5
  max_n: 50
6
  min_p_ratio: 0.1
7
  max_p_ratio: 0.4
8
+ max_steps_scale: 0.5
9
+ tabu_time: 3
10
  tabu_stable_steps_scale: 0.2
11
  popstar: false
12
 
13
  # evaluation
14
  eval_specs:
15
+ region:
16
  seed: 12345
17
  max_nodes: 2488
18
  max_edges: 5000
facility_location/cfg/popstar.yaml DELETED
@@ -1,63 +0,0 @@
1
- # env
2
- env_specs:
3
- region:
4
- min_n: 20
5
- max_n: 50
6
- min_p_ratio: 0.1
7
- max_p_ratio: 0.4
8
- max_steps_scale: 3
9
- tabu_time: 3
10
- tabu_stable_steps_scale: 0.1
11
- popstar: False
12
-
13
- # evaluation
14
- eval_specs:
15
- region:
16
- seed: 12345
17
- val_num_cases: 100
18
- test_num_cases: 100
19
- val_np: !!python/tuple [50, 10]
20
- test_np:
21
- - !!python/tuple [50, 5]
22
- - !!python/tuple [100, 10]
23
- - !!python/tuple [400, 50]
24
-
25
- # agent
26
- agent_specs:
27
- policy_feature_dim: 32
28
- value_feature_dim: 32
29
- policy_hidden_units: !!python/tuple [32, 32, 1]
30
- value_hidden_units: !!python/tuple [32, 32, 1]
31
-
32
- # mlp
33
- mlp_specs:
34
- hidden_units: !!python/tuple [32, 32]
35
-
36
- gnn_specs:
37
- num_gnn_layers: 2
38
- node_dim: 32
39
-
40
-
41
- # ts
42
- ts_specs:
43
- max_steps_scale: 2
44
- stable_iterations_scale: 0.2
45
-
46
-
47
- # popstar
48
- popstar_specs:
49
- graspit: 32
50
- elite: 10
51
-
52
-
53
- # ga
54
- ga_specs:
55
- num_generations: 100
56
- num_parents_mating: 50
57
- sol_per_pop: 100
58
- parent_selection_type: sss
59
- crossover_probability: 0.8
60
- mutation_probability: 0.1
61
-
62
-
63
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
facility_location/cfg/scale1.yaml DELETED
@@ -1,63 +0,0 @@
1
- # env
2
- env_specs:
3
- region:
4
- min_n: 20
5
- max_n: 50
6
- min_p_ratio: 0.1
7
- max_p_ratio: 0.4
8
- max_steps_scale: 5
9
- tabu_time: 1
10
- tabu_stable_steps_scale: 0.1
11
- popstar: false
12
-
13
- # evaluation
14
- eval_specs:
15
- region:
16
- seed: 12345
17
- val_num_cases: 100
18
- test_num_cases: 100
19
- val_np: !!python/tuple [50, 10]
20
- test_np:
21
- - !!python/tuple [50, 5]
22
- - !!python/tuple [100, 10]
23
- - !!python/tuple [400, 50]
24
-
25
- # agent
26
- agent_specs:
27
- policy_feature_dim: 32
28
- value_feature_dim: 32
29
- policy_hidden_units: !!python/tuple [32, 32, 1]
30
- value_hidden_units: !!python/tuple [32, 32, 1]
31
-
32
- # mlp
33
- mlp_specs:
34
- hidden_units: !!python/tuple [32, 32]
35
-
36
- gnn_specs:
37
- num_gnn_layers: 2
38
- node_dim: 32
39
-
40
-
41
- # ts
42
- ts_specs:
43
- max_steps_scale: 2
44
- stable_iterations_scale: 0.2
45
-
46
-
47
- # popstar
48
- popstar_specs:
49
- graspit: 32
50
- elite: 10
51
-
52
-
53
- # ga
54
- ga_specs:
55
- num_generations: 100
56
- num_parents_mating: 50
57
- sol_per_pop: 100
58
- parent_selection_type: sss
59
- crossover_probability: 0.8
60
- mutation_probability: 0.1
61
-
62
-
63
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
facility_location/cfg/scale5.yaml DELETED
@@ -1,63 +0,0 @@
1
- # env
2
- env_specs:
3
- region:
4
- min_n: 20
5
- max_n: 50
6
- min_p_ratio: 0.1
7
- max_p_ratio: 0.4
8
- max_steps_scale: 5
9
- tabu_time: 3
10
- tabu_stable_steps_scale: 0.1
11
- popstar: false
12
-
13
- # evaluation
14
- eval_specs:
15
- region:
16
- seed: 12345
17
- val_num_cases: 100
18
- test_num_cases: 100
19
- val_np: !!python/tuple [50, 10]
20
- test_np:
21
- - !!python/tuple [50, 5]
22
- - !!python/tuple [100, 10]
23
- - !!python/tuple [400, 50]
24
-
25
- # agent
26
- agent_specs:
27
- policy_feature_dim: 32
28
- value_feature_dim: 32
29
- policy_hidden_units: !!python/tuple [32, 32, 1]
30
- value_hidden_units: !!python/tuple [32, 32, 1]
31
-
32
- # mlp
33
- mlp_specs:
34
- hidden_units: !!python/tuple [32, 32]
35
-
36
- gnn_specs:
37
- num_gnn_layers: 2
38
- node_dim: 32
39
-
40
-
41
- # ts
42
- ts_specs:
43
- max_steps_scale: 2
44
- stable_iterations_scale: 0.2
45
-
46
-
47
- # popstar
48
- popstar_specs:
49
- graspit: 32
50
- elite: 10
51
-
52
-
53
- # ga
54
- ga_specs:
55
- num_generations: 100
56
- num_parents_mating: 50
57
- sol_per_pop: 100
58
- parent_selection_type: sss
59
- crossover_probability: 0.8
60
- mutation_probability: 0.1
61
-
62
-
63
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
facility_location/cfg/tabu0.yaml DELETED
@@ -1,63 +0,0 @@
1
- # env
2
- env_specs:
3
- region:
4
- min_n: 20
5
- max_n: 50
6
- min_p_ratio: 0.1
7
- max_p_ratio: 0.4
8
- max_steps_scale: 3
9
- tabu_time: 0
10
- tabu_stable_steps_scale: 0.1
11
- popstar: false
12
-
13
- # evaluation
14
- eval_specs:
15
- region:
16
- seed: 12345
17
- val_num_cases: 100
18
- test_num_cases: 100
19
- val_np: !!python/tuple [50, 10]
20
- test_np:
21
- - !!python/tuple [50, 5]
22
- - !!python/tuple [100, 10]
23
- - !!python/tuple [400, 50]
24
-
25
- # agent
26
- agent_specs:
27
- policy_feature_dim: 32
28
- value_feature_dim: 32
29
- policy_hidden_units: !!python/tuple [32, 32, 1]
30
- value_hidden_units: !!python/tuple [32, 32, 1]
31
-
32
- # mlp
33
- mlp_specs:
34
- hidden_units: !!python/tuple [32, 32]
35
-
36
- gnn_specs:
37
- num_gnn_layers: 2
38
- node_dim: 32
39
-
40
-
41
- # ts
42
- ts_specs:
43
- max_steps_scale: 2
44
- stable_iterations_scale: 0.2
45
-
46
-
47
- # popstar
48
- popstar_specs:
49
- graspit: 32
50
- elite: 10
51
-
52
-
53
- # ga
54
- ga_specs:
55
- num_generations: 100
56
- num_parents_mating: 50
57
- sol_per_pop: 100
58
- parent_selection_type: sss
59
- crossover_probability: 0.8
60
- mutation_probability: 0.1
61
-
62
-
63
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
facility_location/cfg/tabu5.yaml DELETED
@@ -1,63 +0,0 @@
1
- # env
2
- env_specs:
3
- region:
4
- min_n: 20
5
- max_n: 50
6
- min_p_ratio: 0.1
7
- max_p_ratio: 0.4
8
- max_steps_scale: 3
9
- tabu_time: 5
10
- tabu_stable_steps_scale: 0.1
11
- popstar: false
12
-
13
- # evaluation
14
- eval_specs:
15
- region:
16
- seed: 12345
17
- val_num_cases: 100
18
- test_num_cases: 100
19
- val_np: !!python/tuple [50, 10]
20
- test_np:
21
- - !!python/tuple [50, 5]
22
- - !!python/tuple [100, 10]
23
- - !!python/tuple [400, 50]
24
-
25
- # agent
26
- agent_specs:
27
- policy_feature_dim: 32
28
- value_feature_dim: 32
29
- policy_hidden_units: !!python/tuple [32, 32, 1]
30
- value_hidden_units: !!python/tuple [32, 32, 1]
31
-
32
- # mlp
33
- mlp_specs:
34
- hidden_units: !!python/tuple [32, 32]
35
-
36
- gnn_specs:
37
- num_gnn_layers: 2
38
- node_dim: 32
39
-
40
-
41
- # ts
42
- ts_specs:
43
- max_steps_scale: 2
44
- stable_iterations_scale: 0.2
45
-
46
-
47
- # popstar
48
- popstar_specs:
49
- graspit: 32
50
- elite: 10
51
-
52
-
53
- # ga
54
- ga_specs:
55
- num_generations: 100
56
- num_parents_mating: 50
57
- sol_per_pop: 100
58
- parent_selection_type: sss
59
- crossover_probability: 0.8
60
- mutation_probability: 0.1
61
-
62
-
63
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
facility_location/cfg/uniform.yaml DELETED
@@ -1,63 +0,0 @@
1
- # env
2
- env_specs:
3
- region:
4
- min_n: 20
5
- max_n: 50
6
- min_p_ratio: 0.1
7
- max_p_ratio: 0.4
8
- max_steps_scale: 3
9
- tabu_time: 3
10
- tabu_stable_steps_scale: 0.1
11
- popstar: false
12
-
13
- # evaluation
14
- eval_specs:
15
- region:
16
- seed: 12345
17
- val_num_cases: 100
18
- test_num_cases: 100
19
- val_np: !!python/tuple [50, 10]
20
- test_np:
21
- - !!python/tuple [50, 5]
22
- - !!python/tuple [100, 10]
23
- - !!python/tuple [400, 50]
24
-
25
- # agent
26
- agent_specs:
27
- policy_feature_dim: 32
28
- value_feature_dim: 32
29
- policy_hidden_units: !!python/tuple [32, 32, 1]
30
- value_hidden_units: !!python/tuple [32, 32, 1]
31
-
32
- # mlp
33
- mlp_specs:
34
- hidden_units: !!python/tuple [32, 32]
35
-
36
- gnn_specs:
37
- num_gnn_layers: 2
38
- node_dim: 32
39
-
40
-
41
- # ts
42
- ts_specs:
43
- max_steps_scale: 2
44
- stable_iterations_scale: 0.2
45
-
46
-
47
- # popstar
48
- popstar_specs:
49
- graspit: 32
50
- elite: 10
51
-
52
-
53
- # ga
54
- ga_specs:
55
- num_generations: 100
56
- num_parents_mating: 50
57
- sol_per_pop: 100
58
- parent_selection_type: sss
59
- crossover_probability: 0.8
60
- mutation_probability: 0.1
61
-
62
-
63
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
facility_location/cfg/uniform_debug.yaml DELETED
@@ -1,64 +0,0 @@
1
- # env
2
- env_specs:
3
- min_n: 20
4
- max_n: 50
5
- min_p_ratio: 0.1
6
- max_p_ratio: 0.4
7
- max_steps_scale: 2
8
- tabu_time: 5
9
- tabu_stable_steps_scale: 0.1
10
- popstar: false
11
-
12
- # evaluation
13
- eval_specs:
14
- seed: 12345
15
- val_num_cases: 10
16
- test_num_cases: 1000
17
- val_np: !!python/tuple [50, 10]
18
- test_np:
19
- - !!python/tuple [50, 5]
20
- # - !!python/tuple [100, 10]
21
- # - !!python/tuple [400, 50]
22
-
23
- # agent
24
- agent_specs:
25
- policy_feature_dim: 32
26
- value_feature_dim: 32
27
- policy_hidden_units: !!python/tuple [32, 32, 1]
28
- value_hidden_units: !!python/tuple [32, 32, 1]
29
-
30
- # mlp
31
- mlp_specs:
32
- hidden_units: !!python/tuple [32, 32]
33
-
34
- gnn_specs:
35
- num_gnn_layers: 2
36
- node_dim: 32
37
-
38
-
39
- # ts
40
- ts_specs:
41
- max_steps_scale: 2
42
- stable_iterations_scale: 0.2
43
-
44
-
45
- # popstar
46
- popstar_specs:
47
- graspit: 32
48
- elite: 10
49
-
50
-
51
- # ga
52
- ga_specs:
53
- num_generations: 100
54
- num_parents_mating: 50
55
- sol_per_pop: 100
56
- parent_selection_type: sss
57
- crossover_probability: 0.8
58
- mutation_probability: 0.1
59
-
60
- # tabu
61
- tabu_specs:
62
- tabu_time: 5
63
- tabu_stable_steps_scale: 0.1
64
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
facility_location/env/__pycache__/__init__.cpython-39.pyc CHANGED
Binary files a/facility_location/env/__pycache__/__init__.cpython-39.pyc and b/facility_location/env/__pycache__/__init__.cpython-39.pyc differ
 
facility_location/env/__pycache__/facility_location_client.cpython-310.pyc CHANGED
Binary files a/facility_location/env/__pycache__/facility_location_client.cpython-310.pyc and b/facility_location/env/__pycache__/facility_location_client.cpython-310.pyc differ
 
facility_location/env/__pycache__/facility_location_client.cpython-39.pyc CHANGED
Binary files a/facility_location/env/__pycache__/facility_location_client.cpython-39.pyc and b/facility_location/env/__pycache__/facility_location_client.cpython-39.pyc differ
 
facility_location/env/__pycache__/obs_extractor.cpython-310.pyc CHANGED
Binary files a/facility_location/env/__pycache__/obs_extractor.cpython-310.pyc and b/facility_location/env/__pycache__/obs_extractor.cpython-310.pyc differ
 
facility_location/env/__pycache__/obs_extractor.cpython-39.pyc CHANGED
Binary files a/facility_location/env/__pycache__/obs_extractor.cpython-39.pyc and b/facility_location/env/__pycache__/obs_extractor.cpython-39.pyc differ
 
facility_location/env/__pycache__/pmp.cpython-310.pyc CHANGED
Binary files a/facility_location/env/__pycache__/pmp.cpython-310.pyc and b/facility_location/env/__pycache__/pmp.cpython-310.pyc differ
 
facility_location/env/__pycache__/pmp.cpython-39.pyc CHANGED
Binary files a/facility_location/env/__pycache__/pmp.cpython-39.pyc and b/facility_location/env/__pycache__/pmp.cpython-39.pyc differ
 
facility_location/env/facility_location_client.py CHANGED
@@ -21,7 +21,6 @@ class FacilityLocationClient:
21
 
22
  def set_instance(self, points: np.ndarray, demands: np.ndarray, n: int, p: int, real: bool) -> None:
23
  self._points = points
24
-
25
  self._demands = demands
26
  points_geom = MultiPoint(points)
27
  self._gdf = GeoDataFrame({
@@ -43,8 +42,6 @@ class FacilityLocationClient:
43
  self._loss = np.zeros(self._n)
44
  self._add_time = np.full(self._n, -np.inf)
45
  self._drop_time = np.full(self._n, -np.inf)
46
- # self._max_add_tabu_time = min(self._cfg_tabu_time, self._n - self._p - 2)
47
- # self._max_drop_tabu_time = min(self._cfg_tabu_time, self._p - 2)
48
  self.reset_tabu_time()
49
 
50
  def get_instance(self) -> Tuple[np.ndarray, np.ndarray, int, int]:
@@ -59,48 +56,52 @@ class FacilityLocationClient:
59
  return avg_distance, avg_cost
60
 
61
  def _construct_static_graph(self) -> None:
62
- # w = Voronoi_weights(self._points)
63
- # self._static_graph = w.to_networkx()
64
- # self._edges = np.array(self._static_graph .edges, dtype=np.int64)
65
  self._connection_matrix = kneighbors_graph(self._points, n_neighbors=3, mode="connectivity").toarray()
66
  self._static_graph = nx.from_numpy_matrix(self._connection_matrix)
67
  self._static_edges = np.array(self._static_graph.edges(), dtype=np.int64)
68
 
69
- def _construct_dynamic_graph(self) -> None:
70
  t1 = time.time()
71
  try:
72
  solution_distace_min = np.partition(self._distance_matrix[:, self._solution][self._solution, :], 3, axis=-1)[:,2]
73
  except:
74
- print('np:',self._n, self._p)
75
- print('sm:',self._solution.sum())
76
- print('sol:',np.where(self._solution))
77
- print('t:',self._t)
78
  raise ValueError('stop')
79
  solution_distance_matrix = np.zeros((self._n, self._n))
80
  solution_distance_matrix[:, self._solution] = solution_distace_min
81
  solution_knearest_matrix = np.logical_and(self._distance_matrix < solution_distance_matrix, self._distance_matrix > 0)
82
- old_tabu_mask, new_tabu_mask = self.get_tabu_mask(self._t)
83
- solution_matrix = np.logical_and(np.logical_and(self._solution, old_tabu_mask)[:, None], (np.logical_and(~self._solution, new_tabu_mask)[None, :]))
 
 
 
 
 
 
 
 
 
 
84
  solution_matrix = np.logical_or(solution_matrix, solution_matrix.T)
85
  gainloss_matrix = np.logical_and((self._gain[:, None] > self._loss[None, :]), self._loss[None, :] > 0)
86
  graph_matrix = np.logical_and(solution_matrix, np.logical_or(gainloss_matrix, solution_knearest_matrix))
87
 
88
  if not np.any(graph_matrix):
89
- print('Warning: graph_matrix is empty!')
90
- print('np:',self._n, self._p)
91
- print('sm:',solution_matrix.sum())
92
- print('glm:',gainloss_matrix.sum())
93
- print('skm:',solution_knearest_matrix.sum())
94
- print('sol:',np.where(self._solution))
95
- print('old:',np.where(~old_tabu_mask))
96
- print('new:',np.where(~new_tabu_mask))
97
- print('t:',self._t)
98
-
99
  if np.any(solution_matrix):
100
  graph_matrix = solution_matrix
101
  if not np.any(graph_matrix):
102
  raise ValueError('Invalid graph_matrix')
103
-
 
 
 
 
 
 
 
 
 
 
 
104
  self._dynamic_graph = nx.from_numpy_matrix(graph_matrix)
105
  self._dynamic_edges = np.array(self._dynamic_graph.edges(), dtype=np.int64)
106
 
@@ -114,14 +115,6 @@ class FacilityLocationClient:
114
  def get_dynamic_adjacency_list(self) -> np.ndarray:
115
  return self._dynamic_edges
116
 
117
- # def get_degree(self) -> np.ndarray:
118
- # return np.array(self._static_graph .degree)[:, 1]
119
-
120
- # def get_centrality(self) -> Tuple[np.ndarray, np.ndarray]:
121
- # closeness = np.array(list(nx.closeness_centrality(self._static_graph).values()))
122
- # betweenness = np.array(list(nx.betweenness_centrality(self._static_graph).values()))
123
- # return closeness, betweenness
124
-
125
  def compute_initial_solution(self) -> Tuple[float, np.ndarray]:
126
  self._solution = np.zeros(self._n, dtype=bool)
127
  p_0 = self._demands.argmax()
@@ -137,16 +130,12 @@ class FacilityLocationClient:
137
 
138
  def compute_obj_value(self) -> float:
139
  obj_value = self._cost_matrix[:, self._solution].min(axis=-1).sum()
140
- # import pickle
141
- # name = sum(self._solution)
142
- # pickle.dump(self._solution, open(f'/data2/suhongyuan/flp/data/solution/{name}.pkl', 'wb'))
143
- # print('save')
144
  return obj_value
145
 
146
- def compute_obj_value_from_solution(self, solution) -> float:
147
  self._solution = solution
148
  self._init_gain_and_loss()
149
- self._construct_dynamic_graph()
150
  obj_value = self.compute_obj_value()
151
  return obj_value
152
 
@@ -166,8 +155,9 @@ class FacilityLocationClient:
166
  # self._t = t
167
  # return self.compute_obj_value(), self._solution, {}
168
 
169
- def swap(self, facility_pair_index: int, t: int) -> Tuple[float, np.ndarray, Dict]:
170
  facility_pair = self._dynamic_edges[facility_pair_index]
 
171
  facility1 = facility_pair[0]
172
  facility2 = facility_pair[1]
173
 
@@ -178,21 +168,24 @@ class FacilityLocationClient:
178
  new_facility = facility2
179
  old_facility = facility1
180
  else:
181
- print(np.where(self._solution))
182
- warn_msg = f'Facility pair {facility_pair} is not a valid pair.'
183
- print(warn_msg)
184
- print(self._solution[facility1], self._solution[facility2])
185
- print(self._dynamic_graph.has_edge(facility1, facility2))
186
  raise ValueError('stop')
187
 
188
  self._solution[old_facility] = False
189
  self._solution[new_facility] = True
190
- self._old_facility_mask[new_facility] = True
191
- self._new_facility_mask[old_facility] = True
 
 
 
 
192
  self._drop_time[old_facility] = t
193
  self._add_time[new_facility] = t
194
  self._t = t
195
- self._update_env(new_facility, old_facility)
 
 
 
 
196
  # print('st:',self._t)
197
  return self.compute_obj_value(), self._solution, {}
198
 
@@ -251,9 +244,9 @@ class FacilityLocationClient:
251
  self._init_gain_and_loss()
252
  self._construct_dynamic_graph()
253
 
254
- def _update_env(self, insert_facility, remove_facility):
255
  self._update_gain_and_loss(insert_facility, remove_facility)
256
- self._construct_dynamic_graph()
257
 
258
  def _init_gain_and_loss(self):
259
  t1 = time.time()
@@ -274,8 +267,8 @@ class FacilityLocationClient:
274
  # print('init gainloss time:',t2-t1)
275
 
276
  def _update_gain_and_loss(self, insert_facility, remove_facility):
277
- self._init_gain_and_loss()
278
- return
279
 
280
  t1 = time.time()
281
 
 
21
 
22
  def set_instance(self, points: np.ndarray, demands: np.ndarray, n: int, p: int, real: bool) -> None:
23
  self._points = points
 
24
  self._demands = demands
25
  points_geom = MultiPoint(points)
26
  self._gdf = GeoDataFrame({
 
42
  self._loss = np.zeros(self._n)
43
  self._add_time = np.full(self._n, -np.inf)
44
  self._drop_time = np.full(self._n, -np.inf)
 
 
45
  self.reset_tabu_time()
46
 
47
  def get_instance(self) -> Tuple[np.ndarray, np.ndarray, int, int]:
 
56
  return avg_distance, avg_cost
57
 
58
  def _construct_static_graph(self) -> None:
 
 
 
59
  self._connection_matrix = kneighbors_graph(self._points, n_neighbors=3, mode="connectivity").toarray()
60
  self._static_graph = nx.from_numpy_matrix(self._connection_matrix)
61
  self._static_edges = np.array(self._static_graph.edges(), dtype=np.int64)
62
 
63
+ def _construct_dynamic_graph(self,stage=1) -> None:
64
  t1 = time.time()
65
  try:
66
  solution_distace_min = np.partition(self._distance_matrix[:, self._solution][self._solution, :], 3, axis=-1)[:,2]
67
  except:
 
 
 
 
68
  raise ValueError('stop')
69
  solution_distance_matrix = np.zeros((self._n, self._n))
70
  solution_distance_matrix[:, self._solution] = solution_distace_min
71
  solution_knearest_matrix = np.logical_and(self._distance_matrix < solution_distance_matrix, self._distance_matrix > 0)
72
+ if stage == 2:
73
+ old_facility_mask, new_facility_mask = self.get_facility_mask()
74
+ solution_matrix = np.logical_and(np.logical_and(self._solution, old_facility_mask)[:, None], (np.logical_and(~self._solution, new_facility_mask)[None, :]))
75
+ # print('solution:',self._solution)
76
+ # print('old_facility_mask:',old_facility_mask)
77
+ # print('new_facility_mask:',new_facility_mask)
78
+ else:
79
+ old_tabu_mask, new_tabu_mask = self.get_tabu_mask(self._t)
80
+ solution_matrix = np.logical_and(np.logical_and(self._solution, old_tabu_mask)[:, None], (np.logical_and(~self._solution, new_tabu_mask)[None, :]))
81
+ # print('solution:',self._solution)
82
+ # print('old_tabu_mask:',old_tabu_mask)
83
+ # print('new_tabu_mask:',new_tabu_mask)
84
  solution_matrix = np.logical_or(solution_matrix, solution_matrix.T)
85
  gainloss_matrix = np.logical_and((self._gain[:, None] > self._loss[None, :]), self._loss[None, :] > 0)
86
  graph_matrix = np.logical_and(solution_matrix, np.logical_or(gainloss_matrix, solution_knearest_matrix))
87
 
88
  if not np.any(graph_matrix):
 
 
 
 
 
 
 
 
 
 
89
  if np.any(solution_matrix):
90
  graph_matrix = solution_matrix
91
  if not np.any(graph_matrix):
92
  raise ValueError('Invalid graph_matrix')
93
+ else:
94
+ # if stage==2:
95
+ # print('[!] No solution_matrix')
96
+ # print('solution:',self._solution)
97
+ # print('old_facility_mask:',old_facility_mask)
98
+ # print('new_facility_mask:',new_facility_mask)
99
+ # else:
100
+ # print('[!] No solution_matrix')
101
+ # print('solution:',self._solution)
102
+ # print('old_tabu_mask:',old_tabu_mask)
103
+ # print('new_tabu_mask:',new_tabu_mask)
104
+ graph_matrix = self._solution[:, None] ^ self._solution[None, :]
105
  self._dynamic_graph = nx.from_numpy_matrix(graph_matrix)
106
  self._dynamic_edges = np.array(self._dynamic_graph.edges(), dtype=np.int64)
107
 
 
115
  def get_dynamic_adjacency_list(self) -> np.ndarray:
116
  return self._dynamic_edges
117
 
 
 
 
 
 
 
 
 
118
  def compute_initial_solution(self) -> Tuple[float, np.ndarray]:
119
  self._solution = np.zeros(self._n, dtype=bool)
120
  p_0 = self._demands.argmax()
 
130
 
131
  def compute_obj_value(self) -> float:
132
  obj_value = self._cost_matrix[:, self._solution].min(axis=-1).sum()
 
 
 
 
133
  return obj_value
134
 
135
+ def compute_obj_value_from_solution(self, solution, stage=1) -> float:
136
  self._solution = solution
137
  self._init_gain_and_loss()
138
+ self._construct_dynamic_graph(stage)
139
  obj_value = self.compute_obj_value()
140
  return obj_value
141
 
 
155
  # self._t = t
156
  # return self.compute_obj_value(), self._solution, {}
157
 
158
+ def swap(self, facility_pair_index: int, t: int, stage=1) -> Tuple[float, np.ndarray, Dict]:
159
  facility_pair = self._dynamic_edges[facility_pair_index]
160
+ # print(facility_pair)
161
  facility1 = facility_pair[0]
162
  facility2 = facility_pair[1]
163
 
 
168
  new_facility = facility2
169
  old_facility = facility1
170
  else:
 
 
 
 
 
171
  raise ValueError('stop')
172
 
173
  self._solution[old_facility] = False
174
  self._solution[new_facility] = True
175
+ if stage == 1:
176
+ self._old_facility_mask[new_facility] = False
177
+ self._new_facility_mask[old_facility] = True
178
+ else:
179
+ self._old_facility_mask[new_facility] = False
180
+ self._new_facility_mask[old_facility] = False
181
  self._drop_time[old_facility] = t
182
  self._add_time[new_facility] = t
183
  self._t = t
184
+ self._solution[old_facility] = False
185
+ self._solution[new_facility] = True
186
+ # print(self._solution,old_facility,new_facility)
187
+ self._update_env(new_facility, old_facility, stage)
188
+
189
  # print('st:',self._t)
190
  return self.compute_obj_value(), self._solution, {}
191
 
 
244
  self._init_gain_and_loss()
245
  self._construct_dynamic_graph()
246
 
247
+ def _update_env(self, insert_facility, remove_facility, stage):
248
  self._update_gain_and_loss(insert_facility, remove_facility)
249
+ self._construct_dynamic_graph(stage)
250
 
251
  def _init_gain_and_loss(self):
252
  t1 = time.time()
 
267
  # print('init gainloss time:',t2-t1)
268
 
269
  def _update_gain_and_loss(self, insert_facility, remove_facility):
270
+ # self._init_gain_and_loss()
271
+ # return
272
 
273
  t1 = time.time()
274
 
facility_location/env/obs_extractor.py CHANGED
@@ -29,11 +29,8 @@ class ObsExtractor:
29
  virtual_node_x = 0.5
30
  virtual_node_y = 0.5
31
  virtual_node_demand = 1
32
- # virtual_node_degree = 1
33
  virtual_node_avg_distance = 0
34
  virtual_node_avg_cost = 0
35
- # virtual_node_closeness_centrality = 1
36
- # virtual_node_betweenness_centrality = 1
37
  self._virtual_dynamic_node_feature = np.array([
38
  virtual_node_facility,
39
  virtual_node_distance_min,
@@ -47,11 +44,8 @@ class ObsExtractor:
47
  virtual_node_x,
48
  virtual_node_y,
49
  virtual_node_demand,
50
- # virtual_node_degree,
51
  virtual_node_avg_distance,
52
  virtual_node_avg_cost,
53
- # virtual_node_closeness_centrality,
54
- # virtual_node_betweenness_centrality,
55
  ], dtype=np.float32)
56
  self._virtual_node_feature = np.concatenate([
57
  self._virtual_dynamic_node_feature,
@@ -79,23 +73,15 @@ class ObsExtractor:
79
  print(n, self._node_range)
80
  # raise ValueError('The number of nodes exceeds the maximum limit.')
81
  self._n = n
82
- # degree = self._flc.get_degree()
83
- # degree = degree/np.max(degree)
84
  avg_distance, avg_cost = self._flc.get_avg_distance_and_cost()
85
  avg_distance = avg_distance / np.max(avg_distance)
86
  avg_cost = avg_cost / np.max(avg_cost)
87
- # closeness_centrality, betweenness_centrality = self._flc.get_centrality()
88
- # closeness_centrality = closeness_centrality/np.max(closeness_centrality)
89
- # betweenness_centrality = betweenness_centrality/np.max(betweenness_centrality)
90
  self._static_node_features = np.stack([
91
  xy[:, 0],
92
  xy[:, 1],
93
  demands,
94
- # degree,
95
  avg_distance,
96
  avg_cost,
97
- # closeness_centrality,
98
- # betweenness_centrality,
99
  ], axis=-1).astype(np.float32)
100
  static_adjacency_list = self._flc.get_static_adjacency_list()
101
 
@@ -119,8 +105,6 @@ class ObsExtractor:
119
  def get_obs(self, t: int) -> Dict:
120
  obs_nodes, obs_static_edges, obs_dynamic_edges, \
121
  obs_node_mask, obs_static_edge_mask, obs_dynamic_edges_mask = self._get_obs_graph()
122
- # obs_old_facility_mask, obs_new_facility_mask = self._get_obs_action_mask(t)
123
-
124
  obs = {
125
  'node_features': obs_nodes,
126
  'static_adjacency_list': obs_static_edges,
@@ -128,9 +112,8 @@ class ObsExtractor:
128
  'node_mask': obs_node_mask,
129
  'static_edge_mask': obs_static_edge_mask,
130
  'dynamic_edge_mask': obs_dynamic_edges_mask,
131
- # 'old_facility_mask': obs_old_facility_mask,
132
- # 'new_facility_mask': obs_new_facility_mask,
133
  }
 
134
  return obs
135
 
136
  def _get_obs_graph(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
@@ -166,7 +149,6 @@ class ObsExtractor:
166
  # return obs_nodes, obs_static_edges, obs_node_mask, obs_edge_mask
167
 
168
  def _get_obs_action_mask(self, t: int) -> Tuple[np.ndarray, np.ndarray]:
169
- # facility_mask = self._flc.get_current_solution()
170
  old_facility_mask, new_facility_mask = self._flc.get_facility_mask()
171
  old_tabu_mask, new_tabu_mask = self._flc.get_tabu_mask(t)
172
  self._old_facility_mask[1:self._n+1] = np.logical_and(old_facility_mask, old_tabu_mask)
 
29
  virtual_node_x = 0.5
30
  virtual_node_y = 0.5
31
  virtual_node_demand = 1
 
32
  virtual_node_avg_distance = 0
33
  virtual_node_avg_cost = 0
 
 
34
  self._virtual_dynamic_node_feature = np.array([
35
  virtual_node_facility,
36
  virtual_node_distance_min,
 
44
  virtual_node_x,
45
  virtual_node_y,
46
  virtual_node_demand,
 
47
  virtual_node_avg_distance,
48
  virtual_node_avg_cost,
 
 
49
  ], dtype=np.float32)
50
  self._virtual_node_feature = np.concatenate([
51
  self._virtual_dynamic_node_feature,
 
73
  print(n, self._node_range)
74
  # raise ValueError('The number of nodes exceeds the maximum limit.')
75
  self._n = n
 
 
76
  avg_distance, avg_cost = self._flc.get_avg_distance_and_cost()
77
  avg_distance = avg_distance / np.max(avg_distance)
78
  avg_cost = avg_cost / np.max(avg_cost)
 
 
 
79
  self._static_node_features = np.stack([
80
  xy[:, 0],
81
  xy[:, 1],
82
  demands,
 
83
  avg_distance,
84
  avg_cost,
 
 
85
  ], axis=-1).astype(np.float32)
86
  static_adjacency_list = self._flc.get_static_adjacency_list()
87
 
 
105
  def get_obs(self, t: int) -> Dict:
106
  obs_nodes, obs_static_edges, obs_dynamic_edges, \
107
  obs_node_mask, obs_static_edge_mask, obs_dynamic_edges_mask = self._get_obs_graph()
 
 
108
  obs = {
109
  'node_features': obs_nodes,
110
  'static_adjacency_list': obs_static_edges,
 
112
  'node_mask': obs_node_mask,
113
  'static_edge_mask': obs_static_edge_mask,
114
  'dynamic_edge_mask': obs_dynamic_edges_mask,
 
 
115
  }
116
+
117
  return obs
118
 
119
  def _get_obs_graph(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
 
149
  # return obs_nodes, obs_static_edges, obs_node_mask, obs_edge_mask
150
 
151
  def _get_obs_action_mask(self, t: int) -> Tuple[np.ndarray, np.ndarray]:
 
152
  old_facility_mask, new_facility_mask = self._flc.get_facility_mask()
153
  old_tabu_mask, new_tabu_mask = self._flc.get_tabu_mask(t)
154
  self._old_facility_mask[1:self._n+1] = np.logical_and(old_facility_mask, old_tabu_mask)
facility_location/env/tests/p-median.ipynb DELETED
The diff for this file is too large to render. See raw diff
 
facility_location/env/tests/render.ipynb DELETED
The diff for this file is too large to render. See raw diff
 
facility_location/env/utils/env_test.ipynb DELETED
The diff for this file is too large to render. See raw diff
 
facility_location/eval.py DELETED
@@ -1,234 +0,0 @@
1
- import os
2
- import pickle
3
-
4
- import setproctitle
5
- from absl import app, flags
6
- import time
7
- import random
8
- from typing import Tuple, Union, Text
9
-
10
- import numpy as np
11
- import torch as th
12
-
13
- import sys
14
- import gymnasium
15
- sys.modules["gym"] = gymnasium
16
-
17
- from stable_baselines3.common.evaluation import evaluate_policy
18
- from stable_baselines3 import PPO
19
- from stable_baselines3.common.monitor import Monitor
20
- from stable_baselines3.common.vec_env import DummyVecEnv, VecEnvWrapper
21
-
22
- from facility_location.agent.solver import PMPSolver
23
- from facility_location.agent.ga import PMPGA
24
- from facility_location.agent.heuristic import HeuristicRandom, HeuristicGreedy, HeuristicFastInterchange
25
- from facility_location.agent.metaheuristic import TabuSearch, POPSTAR, VNS
26
- from facility_location.env import EvalPMPEnv
27
- from facility_location.utils import Config
28
- from facility_location.agent import MaskedFacilityLocationActorCriticPolicy
29
- from facility_location.utils.policy import get_policy_kwargs
30
-
31
- import warnings
32
- warnings.filterwarnings('ignore')
33
-
34
- flags.DEFINE_string('cfg', None, 'Configuration file.')
35
- flags.DEFINE_integer('global_seed', None, 'Used in env and weight initialization, does not impact action sampling.')
36
- flags.DEFINE_string('root_dir', '/data2/suhongyuan/flp', 'Root directory for writing '
37
- 'logs/summaries/checkpoints.')
38
- flags.DEFINE_bool('tmp', False, 'Whether to use temporary storage.')
39
- flags.DEFINE_enum('agent', None,
40
- ['solver-gurobi', 'solver-gurobi-cmd', 'solver-pulp-cbc-cmd', 'solver-glpk-cmd', 'solver-mosek',
41
- 'heuristic-random', 'heuristic-greedy', 'heuristic-fastinterchange',
42
- 'metaheuristic-ts', 'metaheuristic-vns', 'metaheuristic-popstar',
43
- 'ga',
44
- 'ppo-random',
45
- 'rl-mlp', 'rl-gnn', 'rl-agnn'],
46
- 'Agent type.')
47
- flags.DEFINE_string('model_path', None, 'Path to saved mode to evaluate.')
48
-
49
- FLAGS = flags.FLAGS
50
-
51
-
52
- AGENT = Union[PMPSolver, HeuristicRandom, HeuristicGreedy, HeuristicFastInterchange,
53
- TabuSearch, VNS, POPSTAR, PMPGA, PPO]
54
- BASELINE = Union[PMPSolver, HeuristicRandom, HeuristicGreedy, HeuristicFastInterchange,
55
- TabuSearch, VNS, POPSTAR, PMPGA]
56
-
57
-
58
- def get_model(cfg: Config,
59
- env: Union[VecEnvWrapper, DummyVecEnv, EvalPMPEnv],
60
- device: str) -> PPO:
61
- policy_kwargs = get_policy_kwargs(cfg)
62
- model = PPO(MaskedFacilityLocationActorCriticPolicy,
63
- env,
64
- verbose=1,
65
- policy_kwargs=policy_kwargs,
66
- device=device)
67
- return model
68
-
69
-
70
- def get_agent(cfg: Config,
71
- env: Union[VecEnvWrapper, DummyVecEnv, EvalPMPEnv],
72
- model_path: Text) -> AGENT:
73
- if cfg.agent.startswith('solver'):
74
- if cfg.agent == 'solver-gurobi':
75
- agent = PMPSolver('GUROBI', env)
76
- elif cfg.agent == 'solver-gurobi-cmd':
77
- agent = PMPSolver('GUROBI_CMD', env)
78
- elif cfg.agent == 'solver-pulp-cbc-cmd':
79
- agent = PMPSolver('PULP_CBC_CMD', env)
80
- elif cfg.agent == 'solver-glpk-cmd':
81
- agent = PMPSolver('GLPK_CMD', env)
82
- elif cfg.agent == 'solver-mosek':
83
- agent = PMPSolver('MOSEK', env)
84
- else:
85
- raise ValueError(f'Agent {cfg.agent} not supported.')
86
- elif cfg.agent.startswith('heuristic'):
87
- if cfg.agent == 'heuristic-random':
88
- agent = HeuristicRandom(cfg.seed, env)
89
- elif cfg.agent == 'heuristic-greedy':
90
- agent = HeuristicGreedy(env)
91
- elif cfg.agent == 'heuristic-fastinterchange':
92
- agent = HeuristicFastInterchange(env)
93
- else:
94
- raise ValueError(f'Agent {cfg.agent} not supported.')
95
- elif cfg.agent.startswith('metaheuristic'):
96
- if cfg.agent == 'metaheuristic-ts':
97
- agent = TabuSearch(cfg, env)
98
- elif cfg.agent == 'metaheuristic-vns':
99
- agent = VNS(env)
100
- elif cfg.agent == 'metaheuristic-popstar':
101
- agent = POPSTAR(cfg, env)
102
- else:
103
- raise ValueError(f'Agent {cfg.agent} not supported.')
104
- elif cfg.agent == 'ga':
105
- agent = PMPGA(cfg, env)
106
- elif cfg.agent == 'ppo-random':
107
- agent = PPO("MultiInputPolicy", env, verbose=1)
108
- elif cfg.agent in ['rl-mlp', 'rl-gnn', 'rl-agnn']:
109
- test_model = get_model(cfg, env, device='cuda:3')
110
- trained_model = PPO.load(model_path)
111
- test_model.set_parameters(trained_model.get_parameters())
112
- agent = test_model
113
- else:
114
- raise ValueError(f'Agent {cfg.agent} not supported.')
115
- return agent
116
-
117
-
118
- def evaluate(agent: AGENT,
119
- env: Union[VecEnvWrapper, DummyVecEnv, EvalPMPEnv],
120
- num_cases: int,
121
- return_episode_rewards: bool):
122
- if isinstance(agent, PPO):
123
- return evaluate_ppo(agent, env, num_cases, return_episode_rewards=return_episode_rewards)
124
- else:
125
- return evaluate_baseline(agent, env, num_cases)
126
-
127
- from stable_baselines3.common.callbacks import BaseCallback
128
-
129
-
130
- def evaluate_ppo(agent: PPO, env: EvalPMPEnv, num_cases: int, return_episode_rewards: bool) -> Tuple[float, float]:
131
- # class BestSolutionCallback(BaseCallback):
132
- # def __init__(self, env, verbose=0):
133
- # super(BestSolutionCallback, self).__init__(verbose)
134
- # self.eval_env = env
135
- # self.best_solution = None
136
- # self.best_reward = float('-inf')
137
-
138
- # def _on_rollout_end(self) -> None:
139
- # current_obj_value = np.min(self.model.env._obj_value)
140
- # current_solution = self.model.env._best_solution
141
-
142
- # if current_obj_value < self.best_obj_value:
143
- # self.best_obj_value = current_obj_value
144
- # self.best_solution = current_solution
145
-
146
- # best_solution_callback = BestSolutionCallback(env)
147
- rewards, _ = evaluate_policy(agent, env, n_eval_episodes=num_cases, return_episode_rewards=return_episode_rewards)
148
- # best_solution = best_solution_callback.best_solution
149
-
150
- return rewards
151
-
152
-
153
- def evaluate_baseline(
154
- agent: BASELINE,
155
- env: EvalPMPEnv,
156
- num_cases: int):
157
- rewards = np.zeros(num_cases)
158
- for case_idx in range(num_cases):
159
- env.reset()
160
- solution = agent.solve()
161
- reward = env.evaluate(solution)
162
- rewards[case_idx] = reward
163
- return rewards
164
-
165
- def calculate_gap(gurobi_obj, method_obj):
166
- method_obj = np.array(method_obj)
167
- gap = (method_obj - gurobi_obj) / gurobi_obj
168
- mean_gap = np.mean(gap)
169
- std_gap = np.std(gap)
170
-
171
- return mean_gap, std_gap
172
-
173
-
174
- def main(_):
175
- setproctitle.setproctitle('rl@suhy')
176
-
177
- th.manual_seed(FLAGS.global_seed)
178
- np.random.seed(FLAGS.global_seed)
179
- random.seed(FLAGS.global_seed)
180
-
181
- cfg = Config(FLAGS.cfg, FLAGS.global_seed, FLAGS.tmp, FLAGS.root_dir, FLAGS.agent, model_path=FLAGS.model_path)
182
-
183
- if cfg.eval_specs['region'] is None:
184
- eval_np = cfg.eval_specs['test_np']
185
- else:
186
- eval_path = './data/{}/pkl'.format(cfg.eval_specs['region'])
187
- files = os.listdir(eval_path)
188
- eval_np = []
189
-
190
- for f in files:
191
- eval_np.append(tuple(map(int, f.split('.')[0].split('_'))))
192
- eval_np = sorted(eval_np, key=lambda x: (x[0], x[1]))
193
-
194
- for (n, p) in eval_np:
195
- print(f'case ({n}, {p}):')
196
- eval_env = EvalPMPEnv(cfg, 'test', (n, p))
197
- eval_num_cases = eval_env.get_eval_num_cases()
198
-
199
- if cfg.agent in ['rl-mlp', 'rl-gnn', 'rl-agnn']:
200
- eval_env = Monitor(eval_env)
201
- eval_env = DummyVecEnv([lambda: eval_env])
202
- model_path = os.path.join(cfg.root_dir, 'output', FLAGS.model_path)
203
-
204
- else:
205
- model_path = None
206
-
207
- agent = get_agent(cfg, eval_env, model_path)
208
-
209
- start_time = time.time()
210
- episode_rewards = evaluate(agent, eval_env, eval_num_cases, return_episode_rewards=True)
211
- eval_time = time.time() - start_time
212
-
213
- if cfg.agent == 'solver-gurobi':
214
- pickle.dump(episode_rewards, open(f'gurobi_result/{n}_{p}.pkl', 'wb'))
215
- else:
216
- try:
217
- gurobi_obj = pickle.load(open(f'gurobi_result/{n}_{p}.pkl', 'rb'))
218
- mean_gap, std_gap = calculate_gap(gurobi_obj, episode_rewards)
219
- print(f'\t mean gap: {mean_gap}')
220
- print(f'\t std gap: {std_gap}')
221
- except:
222
- pass
223
-
224
- print(f'\t time: {eval_time / eval_num_cases}')
225
-
226
-
227
- if __name__ == '__main__':
228
- flags.mark_flags_as_required([
229
- 'cfg',
230
- 'global_seed',
231
- 'agent'
232
- ])
233
- app.run(main)
234
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
facility_location/multi_eval.py CHANGED
@@ -20,9 +20,6 @@ from stable_baselines3.common.monitor import Monitor
20
  from stable_baselines3.common.vec_env import DummyVecEnv, VecEnvWrapper
21
 
22
  from facility_location.agent.solver import PMPSolver
23
- from facility_location.agent.ga import PMPGA
24
- from facility_location.agent.heuristic import HeuristicRandom, HeuristicGreedy, HeuristicFastInterchange
25
- from facility_location.agent.metaheuristic import TabuSearch, POPSTAR, VNS
26
  from facility_location.env import EvalPMPEnv, MULTIPMP
27
  from facility_location.utils import Config
28
  from facility_location.agent import MaskedFacilityLocationActorCriticPolicy
@@ -31,29 +28,8 @@ from facility_location.utils.policy import get_policy_kwargs
31
  import warnings
32
  warnings.filterwarnings('ignore')
33
 
34
- flags.DEFINE_string('cfg', None, 'Configuration file.')
35
- flags.DEFINE_integer('global_seed', None, 'Used in env and weight initialization, does not impact action sampling.')
36
- flags.DEFINE_string('root_dir', '/data2/suhongyuan/flp', 'Root directory for writing '
37
- 'logs/summaries/checkpoints.')
38
- flags.DEFINE_bool('tmp', False, 'Whether to use temporary storage.')
39
- flags.DEFINE_enum('agent', None,
40
- ['solver-gurobi', 'solver-gurobi-cmd', 'solver-pulp-cbc-cmd', 'solver-glpk-cmd', 'solver-mosek',
41
- 'heuristic-random', 'heuristic-greedy', 'heuristic-fastinterchange',
42
- 'metaheuristic-ts', 'metaheuristic-vns', 'metaheuristic-popstar',
43
- 'ga',
44
- 'ppo-random',
45
- 'rl-mlp', 'rl-gnn', 'rl-agnn'],
46
- 'Agent type.')
47
- flags.DEFINE_string('model_path', None, 'Path to saved mode to evaluate.')
48
-
49
- FLAGS = flags.FLAGS
50
-
51
-
52
- AGENT = Union[PMPSolver, HeuristicRandom, HeuristicGreedy, HeuristicFastInterchange,
53
- TabuSearch, VNS, POPSTAR, PMPGA, PPO]
54
- BASELINE = Union[PMPSolver, HeuristicRandom, HeuristicGreedy, HeuristicFastInterchange,
55
- TabuSearch, VNS, POPSTAR, PMPGA]
56
 
 
57
 
58
  def get_model(cfg: Config,
59
  env: Union[VecEnvWrapper, DummyVecEnv, EvalPMPEnv],
@@ -70,43 +46,8 @@ def get_model(cfg: Config,
70
  def get_agent(cfg: Config,
71
  env: Union[VecEnvWrapper, DummyVecEnv, EvalPMPEnv],
72
  model_path: Text) -> AGENT:
73
- if cfg.agent.startswith('solver'):
74
- if cfg.agent == 'solver-gurobi':
75
- agent = PMPSolver('GUROBI', env)
76
- elif cfg.agent == 'solver-gurobi-cmd':
77
- agent = PMPSolver('GUROBI_CMD', env)
78
- elif cfg.agent == 'solver-pulp-cbc-cmd':
79
- agent = PMPSolver('PULP_CBC_CMD', env)
80
- elif cfg.agent == 'solver-glpk-cmd':
81
- agent = PMPSolver('GLPK_CMD', env)
82
- elif cfg.agent == 'solver-mosek':
83
- agent = PMPSolver('MOSEK', env)
84
- else:
85
- raise ValueError(f'Agent {cfg.agent} not supported.')
86
- elif cfg.agent.startswith('heuristic'):
87
- if cfg.agent == 'heuristic-random':
88
- agent = HeuristicRandom(cfg.seed, env)
89
- elif cfg.agent == 'heuristic-greedy':
90
- agent = HeuristicGreedy(env)
91
- elif cfg.agent == 'heuristic-fastinterchange':
92
- agent = HeuristicFastInterchange(env)
93
- else:
94
- raise ValueError(f'Agent {cfg.agent} not supported.')
95
- elif cfg.agent.startswith('metaheuristic'):
96
- if cfg.agent == 'metaheuristic-ts':
97
- agent = TabuSearch(cfg, env)
98
- elif cfg.agent == 'metaheuristic-vns':
99
- agent = VNS(env)
100
- elif cfg.agent == 'metaheuristic-popstar':
101
- agent = POPSTAR(cfg, env)
102
- else:
103
- raise ValueError(f'Agent {cfg.agent} not supported.')
104
- elif cfg.agent == 'ga':
105
- agent = PMPGA(cfg, env)
106
- elif cfg.agent == 'ppo-random':
107
- agent = PPO("MultiInputPolicy", env, verbose=1)
108
- elif cfg.agent in ['rl-mlp', 'rl-gnn', 'rl-agnn']:
109
- test_model = get_model(cfg, env, device='cuda:3')
110
  trained_model = PPO.load(model_path)
111
  test_model.set_parameters(trained_model.get_parameters())
112
  agent = test_model
@@ -122,7 +63,7 @@ def evaluate(agent: AGENT,
122
  if isinstance(agent, PPO):
123
  return evaluate_ppo(agent, env, num_cases, return_episode_rewards=return_episode_rewards)
124
  else:
125
- return evaluate_baseline(agent, env, num_cases)
126
 
127
  from stable_baselines3.common.callbacks import BaseCallback
128
 
@@ -131,77 +72,25 @@ def evaluate_ppo(agent: PPO, env: EvalPMPEnv, num_cases: int, return_episode_rew
131
  rewards, _ = evaluate_policy(agent, env, n_eval_episodes=num_cases, return_episode_rewards=return_episode_rewards)
132
  return rewards
133
 
134
- def evaluate_baseline(
135
- agent: BASELINE,
136
- env: EvalPMPEnv,
137
- num_cases: int):
138
- rewards = np.zeros(num_cases)
139
- for case_idx in range(num_cases):
140
- env.reset()
141
- solution = agent.solve()
142
- reward = env.evaluate(solution)
143
- rewards[case_idx] = reward
144
- return rewards
145
-
146
- def calculate_gap(gurobi_obj, method_obj):
147
- method_obj = np.array(method_obj)
148
- gap = (method_obj - gurobi_obj) / gurobi_obj
149
- mean_gap = np.mean(gap)
150
- std_gap = np.std(gap)
151
-
152
- return mean_gap, std_gap
153
-
154
-
155
- def main(_):
156
- setproctitle.setproctitle('rl@suhy')
157
 
158
- th.manual_seed(FLAGS.global_seed)
159
- np.random.seed(FLAGS.global_seed)
160
- random.seed(FLAGS.global_seed)
161
-
162
- cfg = Config(FLAGS.cfg, FLAGS.global_seed, FLAGS.tmp, FLAGS.root_dir, FLAGS.agent, model_path=FLAGS.model_path)
163
-
164
- # if cfg.eval_specs['region'] is None:
165
- # eval_np = cfg.eval_specs['test_np']
166
- # else:
167
- # eval_path = './data/{}/pkl'.format(cfg.eval_specs['region'])
168
- # files = os.listdir(eval_path)
169
- # eval_np = []
170
-
171
- # for f in files:
172
- # eval_np.append(tuple(map(int, f.split('.')[0].split('_'))))
173
- # eval_np = sorted(eval_np, key=lambda x: (x[0], x[1]))
174
- eval_env = MULTIPMP(cfg)
175
-
176
- if cfg.agent in ['rl-mlp', 'rl-gnn', 'rl-agnn']:
177
- eval_env = Monitor(eval_env)
178
- eval_env = DummyVecEnv([lambda: eval_env])
179
- model_path = os.path.join(cfg.root_dir, 'output', FLAGS.model_path)
180
- else:
181
- model_path = None
182
 
 
 
 
 
 
183
  agent = get_agent(cfg, eval_env, model_path)
184
  start_time = time.time()
185
- episode_rewards = evaluate(agent, eval_env, 1, return_episode_rewards=True)
186
  eval_time = time.time() - start_time
187
-
188
-
189
- # if cfg.agent == 'solver-gurobi':
190
- # pickle.dump(episode_rewards, open(f'gurobi_result/{n}_{p}.pkl', 'wb'))
191
- # else:
192
- # gurobi_obj = pickle.load(open(f'gurobi_result/{n}_{p}.pkl', 'rb'))
193
- # mean_gap, std_gap = calculate_gap(gurobi_obj, episode_rewards)
194
- # print(f'\t mean gap: {mean_gap}')
195
- # print(f'\t std gap: {std_gap}')
196
- print(f'\t reward: {episode_rewards}')
197
  print(f'\t time: {eval_time}')
198
 
199
 
200
  if __name__ == '__main__':
201
- flags.mark_flags_as_required([
202
- 'cfg',
203
- 'global_seed',
204
- 'agent'
205
- ])
206
  app.run(main)
207
 
 
20
  from stable_baselines3.common.vec_env import DummyVecEnv, VecEnvWrapper
21
 
22
  from facility_location.agent.solver import PMPSolver
 
 
 
23
  from facility_location.env import EvalPMPEnv, MULTIPMP
24
  from facility_location.utils import Config
25
  from facility_location.agent import MaskedFacilityLocationActorCriticPolicy
 
28
  import warnings
29
  warnings.filterwarnings('ignore')
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
+ AGENT = Union[PMPSolver, PPO]
33
 
34
  def get_model(cfg: Config,
35
  env: Union[VecEnvWrapper, DummyVecEnv, EvalPMPEnv],
 
46
  def get_agent(cfg: Config,
47
  env: Union[VecEnvWrapper, DummyVecEnv, EvalPMPEnv],
48
  model_path: Text) -> AGENT:
49
+ if cfg.agent in ['rl-mlp', 'rl-gnn', 'rl-agnn']:
50
+ test_model = get_model(cfg, env, device='cuda:0')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  trained_model = PPO.load(model_path)
52
  test_model.set_parameters(trained_model.get_parameters())
53
  agent = test_model
 
63
  if isinstance(agent, PPO):
64
  return evaluate_ppo(agent, env, num_cases, return_episode_rewards=return_episode_rewards)
65
  else:
66
+ raise ValueError(f'Agent {agent} not supported.')
67
 
68
  from stable_baselines3.common.callbacks import BaseCallback
69
 
 
72
  rewards, _ = evaluate_policy(agent, env, n_eval_episodes=num_cases, return_episode_rewards=return_episode_rewards)
73
  return rewards
74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
 
76
+ def main(data_npy, boost=False):
77
+ th.manual_seed(0)
78
+ np.random.seed(0)
79
+ random.seed(0)
80
+ model_path = './facility_location/best_model.zip'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
+ cfg = Config('plot', 0, False, '/data2/suhongyuan/flp', 'rl-gnn', model_path=model_path)
83
+
84
+ eval_env = MULTIPMP(cfg, data_npy, boost)
85
+ eval_env = Monitor(eval_env)
86
+ eval_env = DummyVecEnv([lambda: eval_env])
87
  agent = get_agent(cfg, eval_env, model_path)
88
  start_time = time.time()
89
+ _ = evaluate(agent, eval_env, 1, return_episode_rewards=True)
90
  eval_time = time.time() - start_time
 
 
 
 
 
 
 
 
 
 
91
  print(f'\t time: {eval_time}')
92
 
93
 
94
  if __name__ == '__main__':
 
 
 
 
 
95
  app.run(main)
96
 
facility_location/solutions.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:24db38dd59e0613dcf5e2715a1cf875ed47ca74c7c8785c9e588d0f176b62525
3
+ size 2289
facility_location/test.ipynb DELETED
@@ -1,425 +0,0 @@
1
- {
2
- "cells": [
3
- {
4
- "cell_type": "code",
5
- "execution_count": 2,
6
- "metadata": {},
7
- "outputs": [
8
- {
9
- "name": "stdout",
10
- "output_type": "stream",
11
- "text": [
12
- "[[0. 0. 0. ... 0. 0. 0.]\n",
13
- " [0. 0. 0. ... 0. 0. 0.]\n",
14
- " [0. 0. 0. ... 0. 0. 0.]\n",
15
- " ...\n",
16
- " [0. 0. 0. ... 0. 0. 0.]\n",
17
- " [0. 0. 0. ... 0. 0. 0.]\n",
18
- " [0. 0. 0. ... 0. 0. 0.]]\n",
19
- "[0, 1.3065473509668304, 0, 0, 0, 0, 0, 0, 0, 0, 1.0119895121655276, 0, 0, 0, 1.8436509672479273, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.1267322635704136, 0, 0, 0, 0, 0, 0, 0, 0, 1.7347805051324074, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.8483172051875574, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.5343659059282944, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.3037638449483402, 0, 0, 0, 0, 1.3504727008855124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.2921978917216865, 0, 0, 0, 1.108479378407742, 1.653393678507642, 0, 0, 1.0881684264537401, 0, 0, 0, 0, 1.0623021918455655, 0, 1.875191256591029, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.6073341680760638, 0, 0, 0, 1.9805523895709318, 0, 0, 1.403634942312521, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.5219421212115591, 0, 1.138657038124882, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.7630849587454187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.9569291222415044, 0, 0, 1.4085844528393465, 0, 0, 0, 0, 0, 1.0585786345260078, 0, 0, 0, 0, 1.736349229376354, 0, 0, 0, 1.8004691226858753, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.9779176202803515, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.2449110835085255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.1868774604112011, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.076155348166279, 0, 1.7387446698801625, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.9171840290289772, 0, 0, 0, 0, 0, 0, 0, 0, 1.716298965206781, 0, 0, 0, 0, 1.1447802494381323, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.0112323315963594, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.3254928410498104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.8373649059065984, 0, 0, 1.5804061346245062, 0, 0, 1.1179927616272543, 1.9732771314115762, 0, 0, 0, 1.0791197901446883, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.4381135247848582, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.9562907397004219, 0, 0, 0, 0, 0, 0, 1.7499225320939327, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.9541760847800393, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.679649307061875, 0, 0, 0, 1.2385975866230923, 0, 0, 0, 0, 0, 0, 1.9803317984826894, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.3671411900289383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.8917328894219043, 0, 0, 0, 1.7099108098933784, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.615200505489025, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.9019674243157896, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.983515852879571, 0, 0, 1.5882742759320192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.1412517855055038, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.8094776841552782, 0, 0, 0, 0, 0, 0, 0, 0, 1.021378272713097, 0, 0, 0, 0, 1.9429265858537925, 0, 0, 0, 1.7935205973004997, 0, 0, 1.6377902540912914, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.8366946607857926, 1.4781106136146915, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.1568968466422547, 1.1573005183296878, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.9279504608405298, 0, 0, 0, 0, 0, 0, 0, 0, 1.5247602593849523, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.0022484037743946, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.676358929570334, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.3344155679542609, 0, 1.0228587657502164, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.4178207321282081, 0, 0, 0, 1.3399631548961843, 0, 0, 0, 0, 1.2014533629641875, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.655018985449563, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.6369690332038644, 1.4444089312928114, 0, 0, 0, 0, 0, 0, 0, 1.5973146475663211, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.2790129140270188, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.157209813742849, 0, 0, 1.8558075169258035, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.1418489101240186, 0, 0, 0, 1.6728632989913017, 0, 0, 0, 0, 1.9733370583105247, 0, 0, 0, 0, 0, 0, 0, 0, 1.2162257115893476, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.8103211556841596, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.4623508697193839, 0, 0, 0, 0, 0, 0, 1.3820356674348546, 0, 0, 0, 0, 0, 0, 0, 0, 1.3796803102948338, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.8783449770955891, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.8283355432966881, 0, 1.0616043707520344, 1.0338315463576362, 0, 0, 0, 0, 0, 1.8075056506854377, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.2382511252086272, 0]\n",
20
- "[0.9041395630814669, 0, 0.08074497862228569, 0.8222853202667209, 0.11502836231273406, 0.12388377758142011, 0.9935610059555571, 0.9500722273849179, 0.2274986811231311, 0.9331586568638208, 0, 0.10197305464416428, 0.8335838629139186, 0.29722262440052416, 0, 0.5570156829360735, 0.5113093524387131, 0.3945705316981616, 0.700834556583633, 0.5959949562111623, 0.9194833740276643, 0.03639432225067596, 0.8619375383697131, 0.40098857082359296, 0.901096383302275, 0.3326271601473464, 0.40648530281448214, 0.19179973417364282, 0.7384218162811385, 0.606170113450668, 0.2034400951553571, 0.7360049055396741, 0.4377924152683431, 0.701894568371572, 0.8384179992672554, 0.36009447101265624, 0.057089388308546374, 0.6471204277567344, 0.5804477088982322, 0.22360844388185153, 0.3907312862968938, 0.8129163728117023, 0.24849371594354686, 0.6143476258617578, 0.964437866304353, 0.4779139542289089, 0.9551424774494233, 0, 0.6587018840348203, 0.7177205710440928, 0.6803073279158656, 0.7596716797549441, 0.2731038875379401, 0.9963450007000443, 0.9338437488860837, 0.6672254590384904, 0, 0.9766142306013038, 0.3336436044180108, 0.8552466414429304, 0.3773010552363938, 0.7982391639938732, 0.15713379655188064, 0.649499265386598, 0.01111038028608069, 0.7359429126326681, 0.3697736282904457, 0.5188659999559949, 0.7312843130094226, 0.6283878578882, 0.5858647019277301, 0.2568404400053579, 0.26637932073974946, 0.08311060097066814, 0.6823101071566782, 0.5117924556973708, 0.7727964428980895, 0.4871347548357592, 0.23944186531581768, 0, 0.4488843107933801, 0.17050240445830211, 0.32758814288161064, 0.9684889557655156, 0.18713628646961955, 0.23512499010283083, 0.6908607124959899, 0.16292687259130212, 0.5908977261164322, 0.2628512420925919, 0.12637272083797602, 0.8524141068494073, 0.40985597531278284, 0.7007238582874573, 0.27977563104156644, 0.8377433260012307, 0.3438029194739557, 0.5029709995253282, 0.13963145534906607, 0.7768401182629143, 0.8888622066634125, 0.5957917668638463, 0, 0.8976957143183216, 0.6196252423577024, 0.6671880681983002, 0.006747063804729003, 0.7333057215752168, 0.6237054709832023, 0.0735936501961536, 0.8078692846944199, 0.29362729097622287, 0, 0.777762899494489, 0.029937563731620598, 0.9423786649864392, 0.8500751414105719, 0, 0.18379075956506563, 0.7771642104693584, 0.2755344339426994, 0.5844115961137218, 0.08557307910973255, 0.6576189712202234, 0.07014222670294057, 0.059942478070522554, 0.21400722036017483, 0.8715741716698897, 0.41683913664736416, 0.34908168135405493, 0.6741233809948677, 0.04643162148635738, 0.41054193179223764, 0, 0.45598799782701915, 0.8055978593685426, 0.9253146540063065, 0, 0, 0.7792616871813225, 0.6943143812539746, 0, 0.1034712744967976, 0.04313120226573264, 0.2751339614831234, 0.37529960202516477, 0, 0.6041632897991356, 0, 0.4377224992464337, 0.848388109456763, 0.7489141481736912, 0.9320581102000423, 0.34383612164271826, 0.8958464142568214, 0.03235173543474967, 0.4298588684324347, 0.7419659608225888, 0.2786508676578431, 0.7371351320924223, 0.8471415929527776, 0.42416311347299496, 0, 0.7054829673373973, 0.7285328523901956, 0.9173548974710188, 0, 0.559696900477303, 0.03522671001076627, 0, 0.9459404485454181, 0.13167389783915762, 0.5403952264688044, 0.4885489232108575, 0.9738268207931763, 0.17282404625847003, 0.9887621562669708, 0.9004641077939243, 0.3973853074935946, 0.8228046331189511, 0, 0.339142358676055, 0, 0.10991076610928008, 0.25036814672138286, 0.748226432068904, 0.27994481089578416, 0.2950666458236678, 0.9318863921563708, 0.37473607423307753, 0.6294638737449255, 0.011647920169922, 0.5818789092384103, 0.20693661728415869, 0.06620671237320896, 0.7561448831436088, 0.8691939036953821, 0.2840024317972576, 0.6977782413872717, 0.6464567063052008, 0.8599739468079631, 0.8903974329001684, 0, 0.1251244494656777, 0.5152328277644084, 0.5180129200004933, 0.8192211258668435, 0.7623893292489352, 0.3511103448182544, 0.9811217812918138, 0.08109105051697707, 0.5215649551033613, 0, 0.08182277889955691, 0.008911978495510176, 0, 0.20936169974897945, 0.9606710222581915, 0.5403241486294552, 0.5500537132411906, 0.9249699069700033, 0, 0.15251204099627724, 0.7105410992845586, 0.5554157563643312, 0.5038499237684088, 0, 0.7346017403533128, 0.25681088681861497, 0.4552903297148797, 0, 0.5324962682506602, 0.3578657866335586, 0.2377353950948864, 0.8740207917956883, 0.7878675498728824, 0.9106232602973864, 0.7399305059477627, 0.5230520293454254, 0.7975270964647226, 0.4494205530516001, 0.7861022185941772, 0.8860061656787065, 0.298510561162151, 0.07792259650385036, 0.6736325460609645, 0.8779086216893747, 0.8496589885454549, 0.12043620656745824, 0.6543672435497736, 0.2856518449684723, 0.7427512962401306, 0.545746723389937, 0.1614960444663296, 0.9985130190537231, 0.5378096758880552, 0.9209592614862138, 0.5492905986503386, 0.22402881267169894, 0.4727283833715792, 0.08284898363778104, 0.6700098678404831, 0.8263663177408445, 0.347954172253078, 0.793957838138496, 0.47686802583853194, 0.027731088228663436, 0.4106428230860101, 0.8580325647370642, 0.30910601836282603, 0.18961134904524657, 0.8618852602762438, 0.14544712498446244, 0.055424540254410126, 0.48040718196410115, 0.8814855320488243, 0.1916350237168961, 0.48463807674801196, 0, 0.8773885694204007, 0.13331555808834306, 0.6792724057017732, 0.5409699556538768, 0.29660415998011946, 0.15066922145256256, 0.4420588634148688, 0.5753406008853933, 0.3319637395192826, 0.2047850794839846, 0.6288737583881022, 0.5907498395934051, 0.8833944488752735, 0.751144322223856, 0.28728278022586895, 0.12124339212180901, 0.03534714877108136, 0.9872456004096749, 0.3877636751200386, 0.42973253874392237, 0, 0.8067921395105366, 0.9683986536960479, 0.01158396954883445, 0.4696422078397753, 0.31360953027159777, 0.9489858352484257, 0.870737321574568, 0.3072468655227133, 0.09829232669040133, 0, 0.8527033281738108, 0.14097552735219554, 0.9030446674312727, 0.5512484360945659, 0.6034029834956395, 0.9409162663369613, 0.06821104586006099, 0.264181560575097, 0.38505376572100125, 0, 0.07142820128527083, 0, 0.8890022111455872, 0.21925741439376267, 0.7040650671360058, 0.4694101807181672, 0.10864738888572867, 0.49977630026616204, 0.5463131559326317, 0.6312467471741934, 0.14410010930014328, 0.28547049694329496, 0.2521182154499988, 0.10754814331182128, 0.6803197530037096, 0.3445923137962905, 0, 0.3943182102707181, 0.9401367431475006, 0.08922097422544173, 0.5122897539003748, 0.160490512561834, 0.9826345903226602, 0.3352822674749646, 0.1809449426346067, 0, 0.07070862051524007, 0.43700917176439535, 0.4019675833768357, 0.5858729584018305, 0, 0.4323754653350079, 0.5435447823573529, 0.5249053364543692, 0.2760924417134334, 0.7918107455486161, 0.3508338752689446, 0.5772386163105846, 0.7836279026145342, 0.5623593108720227, 0.8941759904811595, 0.616344543050291, 0.024039831287841595, 0.8091744939059091, 0.20219385173577875, 0.7352105259606261, 0.7291878413577416, 0.9690568869515104, 0.8639890441392747, 0.26775948568076724, 0.6456125369468928, 0.8633019490227369, 0.46019650427695513, 0.13733931953796363, 0.7009943564550838, 0, 0.8765208071298392, 0.3510481090866573, 0.952741243119236, 0.6085370909733463, 0.060591668877822635, 0.593458806949922, 0.32999824176331993, 0.2005563715407065, 0.6356515525522672, 0.5313310156503008, 0, 0.6526157579542673, 0.16528648691346326, 0.5880637184852238, 0.8403160384815712, 0.7753748108425729, 0.43479104149265213, 0.8075093032887632, 0.24882971412296206, 0.993218223804026, 0.4075145035481623, 0.659057426502469, 0.7096298365432919, 0.38871327370864295, 0, 0.0059850553267807305, 0.0051590124390130665, 0, 0.4339201810744312, 0.6589340924156193, 0, 0, 0.0183747786202757, 0.641728966555246, 0.13788554402047293, 0, 0.04142787222083466, 0.5437210792143448, 0.6682354838913858, 0.4863337541023133, 0.2516239362588567, 0.08413190053059949, 0.9676267580134232, 0.22575297983288822, 0.1244255557152607, 0.8224185681282771, 0.17799363953067182, 0, 0.5025441510675511, 0.5019710265230332, 0.9357275145619265, 0.15836873585980826, 0.5713881585816664, 0.22827904180589342, 0.4775693009965608, 0.6086289931020366, 0.2583546327316848, 0.5103052594959362, 0.7145900448524471, 0.10295397178917542, 0, 0.09275601610146023, 0.6991098891594635, 0.7029764190768392, 0.6212182473669104, 0.5820433796886109, 0.5388736199502143, 0, 0.9944249844077415, 0.40857486403028265, 0.9661269137950269, 0.9403489056304841, 0.6726652292399451, 0.9051335183708998, 0.3164921310764257, 0.9023366050597892, 0.30852888389712896, 0.1533769072061496, 0.05989727443934789, 0.1843064816232104, 0.1798016691253399, 0, 0.6178075591514937, 0.3686226710481947, 0.6184461049971367, 0.8851501336979073, 0.06093147298207546, 0.6256799742565118, 0.7849115120346345, 0.3784104526133262, 0.9371851888020702, 0.684972759585991, 0.7949878962983897, 0.8616055799031592, 0.479746692653625, 0.6124871523062442, 0.10666483557632933, 0.006180345888404548, 0.6735977991008641, 0.10158871183327556, 0.591704934660402, 0.2312087408602581, 0.9815148154265422, 0.8485224448763321, 0.6455132893667194, 0.30405268130718377, 0, 0.6843958862522528, 0.9202546611995617, 0.9155463294525774, 0, 0.2465322719342622, 0.7116900962619911, 0.6852435432761507, 0.36921139065044417, 0.24079805201788684, 0.5279979865872105, 0, 0.4580950598339274, 0.09416817268177735, 0.24485156044670375, 0.6795116598241068, 0.10926978632127848, 0.4291183769079828, 0.3173187695944222, 0.33484985397163214, 0.038161498557721774, 0, 0.8512067172888305, 0.3269439340369171, 0.09063192594923575, 0.21357890043557815, 0.477913461503943, 0.562248630937037, 0.8926437627426074, 0.6478237578129892, 0.48206217605588086, 0.34727516061731833, 0.9133360767841633, 0.9965298351093996, 0.9310818327939521, 0.9203270594874299, 0.427661452528471, 0.08481245152119843, 0.27825465814929184, 0.338289782286603, 0.8310840927876677, 0, 0.9361644149777257, 0.46751272795172805, 0.06916297361837564, 0, 0.2667695364435009, 0.3914438449163915, 0.9089951984770299, 0.26705023736611455, 0.7140484248458, 0.9328715783389795, 0.8784764173296057, 0.43864057572631243, 0.497365474780739, 0, 0.39495852791800456, 0.32556251832409067, 0.04145996851406375, 0.5321255615302387, 0.4745736483959867, 0.8077031225529914, 0.7727182252723452, 0.32350776752668176, 0.8516700737916549, 0.8905590837613401, 0.5935164487881724, 0.34323812504670614, 0.3762190625604799, 0, 0.7122030878318626, 0.7201016629223704, 0.5032402348210122, 0.6459771004940253, 0.28291065795682, 0.9807675129793004, 0.904801321395868, 0.7249312328310027, 0.08797612394667331, 0.21652646788592023, 0.2502698372860521, 0.5271991408875268, 0.3904368367161952, 0, 0.36508174527578696, 0.28549760122806267, 0, 0.6530637713991875, 0.07111060472758746, 0.539713440473812, 0.06379108642341613, 0.4609514585175071, 0.17100240601976935, 0.20616576051652735, 0.6187094701051248, 0.6269357691081149, 0, 0.5281274232165335, 0.13783184906931645, 0.6935570838981316, 0.4056335243279451, 0.7133338668936207, 0.7475108839851734, 0.17357481507151973, 0.5188185089754186, 0.9707935671973664, 0, 0.6135368531681027, 0.20463806463336487, 0.7988704077506983, 0.12569583554972774, 0.9029179052812085, 0.20153927911653413, 0.4953392643904281, 0.31390765664891884, 0, 0.3309013777524318, 0.6270666932243821, 0.7796749710315518, 0.9056785444042665, 0, 0.9531026962094459, 0.2580494188736775, 0.2130379222435661, 0, 0.6404773765646499, 0.5151777681422338, 0, 0.849179519208299, 0.40777786434510566, 0.03250867310816463, 0.4806021833149339, 0.6205728646164946, 0.4925314551993333, 0.8647770115517502, 0.39070743887579995, 0.42416135424755597, 0.54984394800331, 0.6781483425918189, 0.6498463642722709, 0.8563189193468166, 0.25555800305584464, 0.05700199355385749, 0.08965338534965728, 0.15529537842766272, 0.9149924358819634, 0.44384858784509185, 0.27209987746166564, 0.18655130321037405, 0.9874438834501506, 0.09507860989480466, 0, 0, 0.2440388914742504, 0.5256635084984219, 0.16051723098089754, 0.001558650858930366, 0.7487131249747785, 0.5326827482320085, 0.06556091356378457, 0.9924489229483846, 0.0798524142793332, 0.9536950395766158, 0.2291072757089373, 0.1928968621390703, 0, 0, 0.4373574990027175, 0.530806467064814, 0.20721530788094122, 0.8006704059039377, 0.20253089835154503, 0.47704054715261346, 0.1986035873715144, 0.4589304057288962, 0.6465807585041393, 0.7547689418434551, 0, 0.31060381223641076, 0.414218513671307, 0.5931807317923922, 0.18856659632218276, 0.6196167400377182, 0.08000475281653141, 0.8470246747851051, 0.22057370127305498, 0, 0.8342942680720551, 0.028757429285548808, 0.8156362526375006, 0.995845201683645, 0.24145512335842445, 0.5061782487865663, 0.8026965174319032, 0.43506325401396284, 0.5571950697614316, 0.25066087051375796, 0.8381081762722642, 0.15586199008618862, 0, 0.4937714349711336, 0.9645730942911799, 0.5295090560649596, 0.06968803878960395, 0.44685154003828975, 0.33742346829989367, 0.13481478825449678, 0.803920949795903, 0.630963718190743, 0.24798703491148433, 0.8231362409935734, 0.7240923734212911, 0.2846436847806062, 0.05982454796958059, 0.35028500517867445, 0.614081918820337, 0.861616830962007, 0.534818561063319, 0.8847078321709532, 0.5166459644245666, 0, 0.4233714925279961, 0.4099433816129605, 0.3596634270557205, 0.8610567120629865, 0.8518711302938005, 0.9815643847289087, 0.9399628151862728, 0.2769446749682547, 0.26183229274445396, 0.8088448102791975, 0.33687053509902065, 0.7400021939452547, 0, 0.3104827029954591, 0, 0.21970236591471226, 0.41627055271728786, 0.760887088424125, 0.504908631439443, 0.3309286197749345, 0.02719415662268243, 0.8562693107931387, 0.9160753449428137, 0.9215797164219479, 0.5517490913245979, 0.3243906541755075, 0.7787841293892179, 0.9041181979807672, 0, 0.4065020828144216, 0.3263035620019985, 0.21462217259429095, 0, 0.2095263867639977, 0.6957321786683563, 0.8711085360114326, 0.4567387764709292, 0, 0.9103800028392032, 0.03993385756609047, 0.8743740215362584, 0.8520540004186101, 0.3023664438123982, 0.5362319606972364, 0.6852340947034127, 0.14673994613405017, 0.9556076167243608, 0.2599468487198915, 0.6166229242431326, 0.3647502822229911, 0.06970322845198929, 0.8059945397482337, 0.6935906648605085, 0.14484907161927674, 0.7489671614625661, 0.0761100118803788, 0.6929575390278396, 0.411156902510315, 0.5680362145035645, 0.22103038278350906, 0.33378918072616803, 0.07401872093186024, 0, 0.2384710487765187, 0.7537046273865109, 0.5650633058530684, 0.537903467024971, 0.6730136981282536, 0.258276624419172, 0.0481812499641181, 0.16161642109590713, 0.2063293661678547, 0.613858263924941, 0.7401198936147888, 0.6336758174448643, 0.8225024837353339, 0.5978551426718874, 0.09542417674528791, 0.6120833656788707, 0.323903842960925, 0.13931854582455183, 0.5381619902061565, 0.02333574379227754, 0.4216826597745471, 0.8356196870670161, 0.31827308971600765, 0.039285749836145634, 0.8880276700762737, 0, 0, 0.6875430459082899, 0.8412151232415347, 0.7242854452933934, 0.2775442897910889, 0.6389462017857761, 0.043221056775573086, 0.8988220132682924, 0, 0.1959330022168887, 0.8396355706302502, 0.9773002405212339, 0.224356307715603, 0.2803573387059688, 0.5813042002350415, 0.4172020116207842, 0.8985232844254049, 0.7947533403582208, 0.45353379953204853, 0, 0.325408223919478, 0.11257225763441736, 0.47060979677015324, 0.16172279756269536, 0.16494802886054338, 0.36345724739477214, 0.9207933258117182, 0.8186675832402952, 0.8312671423338792, 0.40003451340073903, 0, 0.3556519587605015, 0.6229618810063414, 0, 0.9783244908721367, 0.25559285308007373, 0.45778206830891155, 0.5878524852553565, 0.06799989537861562, 0.2001853230549019, 0.6793707994030119, 0.7150481117705543, 0.577526152899073, 0.4298314283931458, 0.5405489283762733, 0.9083723028981335, 0.9813915128936416, 0.6738361039036571, 0.5871912023825119, 0.23013287609315702, 0.26115915961928027, 0.6822262476747706, 0.17423279385023271, 0.2673150258440812, 0.46048878108024516, 0.14195996747161166, 0.7603060512054939, 0.7363273112341094, 0.41845020429252844, 0.7769527436525356, 0.45434056921894905, 0.5818152963666642, 0.5146678202075496, 0.792670749711979, 0.15426060206288117, 0.2827108066915468, 0.36435578615059594, 0.7639770507184225, 0.9375989691330652, 0.640466561585606, 0.612054008185558, 0.8327635405917856, 0.33531157041415005, 0.6919819159373694, 0.2814933376586002, 0.4280167561341125, 0.9204562423681187, 0, 0.4617258506284855, 0.1687304854159677, 0.21150153046961362, 0, 0.9473641022454827, 0.6199947695650183, 0.36494798871863776, 0.7687437225004924, 0, 0.31596464149129233, 0.3650930095165815, 0.5515049984692568, 0.6648730895561041, 0.9040205803644148, 0.6517095504440272, 0.8259996994449177, 0.08542523283290193, 0, 0.8352731019851729, 0.7203015441232917, 0.871104185117645, 0.42131068249022896, 0.4961835062629919, 0.44103602918847074, 0.5341996553767577, 0.8955804760089646, 0.03519950963156382, 0.5684316886879497, 0.19634651747610699, 0.8858180625145002, 0.4067999310913569, 0.6462912284097071, 0.8450273425899184, 0, 0.4627496072921794, 0.5453573248220742, 0.2288024693436702, 0.43728563713888946, 0.5225489186245426, 0.02675474718827686, 0.5205272619305802, 0.6907443700242047, 0.04073932102277289, 0.693716360144892, 0.32687302381139904, 0.6277895671325022, 0.7593345890756615, 0.3578991326379847, 0.31060606721434136, 0, 0.733087376565599, 0.061341467569715036, 0.35967434041329605, 0.48940154101859323, 0.23583593301173722, 0.7868846146030043, 0, 0.9543888801166363, 0.9255814796677508, 0.5962028744083365, 0.8076491126833315, 0.31571008569147785, 0.9238533846756112, 0.15398713026371935, 0.1952859534354633, 0, 0.725442879082974, 0.7588757369740254, 0.5684667524603941, 0.9179849730776254, 0.25101526871760105, 0.18105423091013084, 0.8435309046984312, 0.23222336038018143, 0.18700649703925543, 0, 0.4491639803654781, 0.6919975904280216, 0.3190004906272401, 0.8736572536181758, 0.10251230834295466, 0.7058530231012046, 0.8978182112867975, 0.73813298121533, 0.8745006564783215, 0.7845526679418063, 0.39191121691254804, 0.6055716295965105, 0.8356709917180716, 0.00288366886400071, 0.6559699987601796, 0.23331256294315594, 0.9776079303483803, 0.09119367760202723, 0.19556751021159646, 0.8363706359031983, 0.9142543696590871, 0.8318105487214865, 0.5926716090135717, 0.3725814516905266, 0.11340419090818132, 0.9645171525488953, 0.11347184903978369, 0.4468986892355996, 0.5396782277129197, 0.6585159819330665, 0.007796835932915469, 0, 0.20052883098350116, 0, 0, 0.9474749905442867, 0.6069534186098525, 0.3208035794554124, 0.8042891168285783, 0.43736320913444793, 0, 0.25436181360882426, 0.7356693659526581, 0.3490849314850649, 0.36254338777723993, 0.2517640014121405, 0.4710453196055221, 0.5775721161180677, 0.205311102057802, 0.029118079438258948, 0.33317546098187434, 0.5541188602042993, 0, 0.22312773319680268]\n",
21
- "[[False False False ... False False False]\n",
22
- " [ True False True ... True False True]\n",
23
- " [False False False ... False False False]\n",
24
- " ...\n",
25
- " [False False False ... False False False]\n",
26
- " [ True False True ... True False True]\n",
27
- " [False False False ... False False False]]\n",
28
- "1857\n"
29
- ]
30
- }
31
- ],
32
- "source": [
33
- "import numpy as np\n",
34
- "from sklearn.neighbors import kneighbors_graph\n",
35
- "import networkx as nx\n",
36
- "n = 1000\n",
37
- "points = np.random.rand(n, 2)\n",
38
- "# init solutions = 1 with p=0.1\n",
39
- "solutions = np.random.choice([0, 1], size=n, p=[0.9, 0.1])\n",
40
- "gain = [0 if i == 0 else np.random.rand() + 1 for i in solutions]\n",
41
- "loss = [0 if i == 1 else np.random.rand() for i in solutions]\n",
42
- "connection_matrix = kneighbors_graph(points, n_neighbors=3, mode=\"connectivity\").toarray()\n",
43
- "print(connection_matrix)\n",
44
- "solution_matrix = (solutions)[:, None] ^ (solutions)[None, :]\n",
45
- "gain_loss_matrix = np.logical_and(np.array(gain)[:, None] > np.array(loss)[None, :], np.array(loss)[None, :])\n",
46
- "print(gain)\n",
47
- "print(loss)\n",
48
- "print(gain_loss_matrix)\n",
49
- "\n",
50
- "final_matrix = np.logical_and(connection_matrix, np.logical_or(gain_loss_matrix, connection_matrix))\n",
51
- "\n",
52
- "G = nx.from_numpy_matrix(final_matrix)\n",
53
- "print(len(G.edges()))"
54
- ]
55
- },
56
- {
57
- "cell_type": "code",
58
- "execution_count": 50,
59
- "metadata": {},
60
- "outputs": [
61
- {
62
- "name": "stdout",
63
- "output_type": "stream",
64
- "text": [
65
- "[ True False True True True]\n",
66
- "[[0 2]\n",
67
- " [2 1]\n",
68
- " [1 2]\n",
69
- " [2 1]\n",
70
- " [3 1]]\n",
71
- "[[0. 0.65806633 0.43679866 0.78755153]\n",
72
- " [0.38478979 0.28579072 0.1175966 0.54196134]\n",
73
- " [0.65806633 0. 0.31616109 0.3822108 ]\n",
74
- " [0.43679866 0.31616109 0. 0.63191089]\n",
75
- " [0.78755153 0.3822108 0.63191089 0. ]]\n",
76
- "[[0. 0.43679866]\n",
77
- " [0.1175966 0.28579072]\n",
78
- " [0. 0.31616109]\n",
79
- " [0. 0.31616109]\n",
80
- " [0. 0.3822108 ]]\n",
81
- "[[0. 0.43679866]\n",
82
- " [0.1175966 0.28579072]\n",
83
- " [0. 0.31616109]\n",
84
- " [0. 0.31616109]\n",
85
- " [0. 0.3822108 ]]\n"
86
- ]
87
- }
88
- ],
89
- "source": [
90
- "# init 10 points \n",
91
- "n = 5\n",
92
- "import numpy as np\n",
93
- "from sklearn.metrics import pairwise_distances\n",
94
- "\n",
95
- "points = np.random.rand(n, 2)\n",
96
- "distance_matrix = pairwise_distances(points)\n",
97
- "for i in range(n):\n",
98
- " for j in range(n):\n",
99
- " distance_matrix[i, j] = np.linalg.norm(points[i] - points[j])\n",
100
- " \n",
101
- "solution = np.random.choice([False, True], size=n, p=[0.5, 0.5])\n",
102
- "print(solution)\n",
103
- "distance2solution = distance_matrix[:, solution]\n",
104
- "mmin = np.partition(distance2solution, 2, axis=-1)[:,:2]\n",
105
- "argpartition = np.argpartition(distance2solution, 2, axis=-1)[:,:2]\n",
106
- "print(argpartition)\n",
107
- "mmin_arg = distance_matrix[:, solution][np.arange(n)[:, None], argpartition]\n",
108
- "# print(distance_matrix)\n",
109
- "print(distance2solution)\n",
110
- "print(mmin)\n",
111
- "# print(argpartition)\n",
112
- "print(mmin_arg)"
113
- ]
114
- },
115
- {
116
- "cell_type": "code",
117
- "execution_count": 8,
118
- "metadata": {},
119
- "outputs": [
120
- {
121
- "name": "stdout",
122
- "output_type": "stream",
123
- "text": [
124
- "[[0. 0.21715016 0.33132471 0.19052463 0.54986648 0.41810508\n",
125
- " 0.75511092 0.19542409 0.50229276 0.3868646 ]\n",
126
- " [0.21715016 0. 0.35967329 0.3556269 0.75465908 0.42573686\n",
127
- " 0.92832302 0.39362976 0.68397764 0.4859949 ]\n",
128
- " [0.33132471 0.35967329 0. 0.51429654 0.58080672 0.08766521\n",
129
- " 1.03866136 0.49096679 0.78676377 0.71801827]\n",
130
- " [0.19052463 0.3556269 0.51429654 0. 0.55490601 0.60192552\n",
131
- " 0.57544324 0.07961631 0.32839122 0.21350254]\n",
132
- " [0.54986648 0.75465908 0.58080672 0.55490601 0. 0.63075728\n",
133
- " 0.7194792 0.4754162 0.54633061 0.72183098]\n",
134
- " [0.41810508 0.42573686 0.08766521 0.60192552 0.63075728 0.\n",
135
- " 1.12283786 0.57809215 0.87181293 0.80495882]\n",
136
- " [0.75511092 0.92832302 1.03866136 0.57544324 0.7194792 1.12283786\n",
137
- " 0. 0.56159289 0.25405459 0.48903412]\n",
138
- " [0.19542409 0.39362976 0.49096679 0.07961631 0.4754162 0.57809215\n",
139
- " 0.56159289 0. 0.3078841 0.27326372]\n",
140
- " [0.50229276 0.68397764 0.78676377 0.32839122 0.54633061 0.87181293\n",
141
- " 0.25405459 0.3078841 0. 0.30239869]\n",
142
- " [0.3868646 0.4859949 0.71801827 0.21350254 0.72183098 0.80495882\n",
143
- " 0.48903412 0.27326372 0.30239869 0. ]]\n",
144
- "[False True True True False True True True False False]\n",
145
- "[[0. 0.35967329 0.3556269 0.42573686 0.92832302 0.39362976]\n",
146
- " [0.35967329 0. 0.51429654 0.08766521 1.03866136 0.49096679]\n",
147
- " [0.3556269 0.51429654 0. 0.60192552 0.57544324 0.07961631]\n",
148
- " [0.42573686 0.08766521 0.60192552 0. 1.12283786 0.57809215]\n",
149
- " [0.92832302 1.03866136 0.57544324 1.12283786 0. 0.56159289]\n",
150
- " [0.39362976 0.49096679 0.07961631 0.57809215 0.56159289 0. ]]\n",
151
- "[0.3556269 0.08766521 0.07961631 0.08766521 0.56159289 0.07961631]\n",
152
- "[[0. 0.3556269 0.08766521 0.07961631 0. 0.08766521\n",
153
- " 0.56159289 0.07961631 0. 0. ]\n",
154
- " [0. 0.3556269 0.08766521 0.07961631 0. 0.08766521\n",
155
- " 0.56159289 0.07961631 0. 0. ]\n",
156
- " [0. 0.3556269 0.08766521 0.07961631 0. 0.08766521\n",
157
- " 0.56159289 0.07961631 0. 0. ]\n",
158
- " [0. 0.3556269 0.08766521 0.07961631 0. 0.08766521\n",
159
- " 0.56159289 0.07961631 0. 0. ]\n",
160
- " [0. 0.3556269 0.08766521 0.07961631 0. 0.08766521\n",
161
- " 0.56159289 0.07961631 0. 0. ]\n",
162
- " [0. 0.3556269 0.08766521 0.07961631 0. 0.08766521\n",
163
- " 0.56159289 0.07961631 0. 0. ]\n",
164
- " [0. 0.3556269 0.08766521 0.07961631 0. 0.08766521\n",
165
- " 0.56159289 0.07961631 0. 0. ]\n",
166
- " [0. 0.3556269 0.08766521 0.07961631 0. 0.08766521\n",
167
- " 0.56159289 0.07961631 0. 0. ]\n",
168
- " [0. 0.3556269 0.08766521 0.07961631 0. 0.08766521\n",
169
- " 0.56159289 0.07961631 0. 0. ]\n",
170
- " [0. 0.3556269 0.08766521 0.07961631 0. 0.08766521\n",
171
- " 0.56159289 0.07961631 0. 0. ]]\n"
172
- ]
173
- }
174
- ],
175
- "source": [
176
- "# init 10 points \n",
177
- "n = 10\n",
178
- "import numpy as np\n",
179
- "from sklearn.metrics import pairwise_distances\n",
180
- "\n",
181
- "points = np.random.rand(n, 2)\n",
182
- "distance_matrix = pairwise_distances(points)\n",
183
- "for i in range(n):\n",
184
- " for j in range(n):\n",
185
- " distance_matrix[i, j] = np.linalg.norm(points[i] - points[j])\n",
186
- " \n",
187
- "solution = np.random.choice([False, True], size=n, p=[0.5, 0.5])\n",
188
- "m = distance_matrix[:, solution][solution, :]\n",
189
- "mmin = np.partition(m, 2, axis=-1)[:,1]\n",
190
- "restore = np.zeros((n, n))\n",
191
- "restore[:, solution] = mmin\n",
192
- "\n",
193
- "print(distance_matrix)\n",
194
- "print(solution)\n",
195
- "print(m)\n",
196
- "print(mmin)\n",
197
- "print(restore)\n",
198
- "\n",
199
- "# 将mmin按照solution恢复原尺寸,false的位置补0列,true的位置补mmin\n"
200
- ]
201
- },
202
- {
203
- "cell_type": "code",
204
- "execution_count": 27,
205
- "metadata": {},
206
- "outputs": [
207
- {
208
- "name": "stdout",
209
- "output_type": "stream",
210
- "text": [
211
- "[1]\n",
212
- "[1]\n"
213
- ]
214
- }
215
- ],
216
- "source": [
217
- "a = [1]\n",
218
- "print(a)\n",
219
- "print(list(a))"
220
- ]
221
- },
222
- {
223
- "cell_type": "code",
224
- "execution_count": 4,
225
- "metadata": {},
226
- "outputs": [
227
- {
228
- "name": "stdout",
229
- "output_type": "stream",
230
- "text": [
231
- "[[False True False True]\n",
232
- " [ True False True False]\n",
233
- " [False True False True]\n",
234
- " [ True False True False]]\n",
235
- "[(0, 1), (0, 3), (1, 2), (2, 3)]\n"
236
- ]
237
- }
238
- ],
239
- "source": [
240
- "import numpy as np\n",
241
- "import networkx as nx\n",
242
- "solution1 = [False, True, False, True]\n",
243
- "solution2 = [True, False, True, False]\n",
244
- "\n",
245
- "# solution_matrix[i][j] = 1 if solution1[i] and !solution2[j]\n",
246
- "solution_matrix = np.logical_and(np.array(solution1)[:, None], np.logical_not(np.array(solution2)[None, :]))\n"
247
- ]
248
- },
249
- {
250
- "cell_type": "code",
251
- "execution_count": 9,
252
- "metadata": {},
253
- "outputs": [
254
- {
255
- "name": "stdout",
256
- "output_type": "stream",
257
- "text": [
258
- "[[ True False True False True True False True True False]\n",
259
- " [ True True False True False False False False False False]\n",
260
- " [ True False False True False True False True False False]\n",
261
- " [ True False False True False True True True True False]\n",
262
- " [False False True False False False False False True False]\n",
263
- " [False False False False False True True False False True]\n",
264
- " [ True False True False False True False True True True]\n",
265
- " [False True True True True False True False False False]\n",
266
- " [False True True True False True False False False True]\n",
267
- " [False True True False False True False True True True]]\n"
268
- ]
269
- }
270
- ],
271
- "source": [
272
- "# random nxn bool\n",
273
- "n = 10\n",
274
- "solution_matrix = np.random.choice([False, True], size=(n, n), p=[0.5, 0.5])\n",
275
- "print(solution_matrix)\n",
276
- "\n",
277
- "# if solution_matrix[i][j] == 1, then solution_matrix[j][i] = 1\n",
278
- "solution_matrix = np.logical_or(solution_matrix, solution_matrix.T)"
279
- ]
280
- },
281
- {
282
- "cell_type": "code",
283
- "execution_count": 1,
284
- "metadata": {},
285
- "outputs": [
286
- {
287
- "name": "stdout",
288
- "output_type": "stream",
289
- "text": [
290
- "[2, 2, 3, 1, 2]\n"
291
- ]
292
- }
293
- ],
294
- "source": [
295
- "a = [\n",
296
- " [True, False, True, False, True],\n",
297
- " [False, True, True, False, False],\n",
298
- " [True, True, True, False, False],\n",
299
- " [False, False, False, True, True]\n",
300
- "]\n",
301
- "\n",
302
- "result = [sum(sublist) for sublist in zip(*a)]\n",
303
- "print(result)\n"
304
- ]
305
- },
306
- {
307
- "cell_type": "code",
308
- "execution_count": 72,
309
- "metadata": {},
310
- "outputs": [
311
- {
312
- "name": "stdout",
313
- "output_type": "stream",
314
- "text": [
315
- "[[ 71.41590974 136.395999 107.69667527 113.67004198 121.34052811]\n",
316
- " [ 8.24500825 16.66169284 150.51499016 100.86207765 189.98448317]\n",
317
- " [ 47.46595456 0.83386271 8.11927175 14.5288237 82.16321367]\n",
318
- " [149.58413362 118.14886729 126.25917039 159.2670266 12.87411618]\n",
319
- " [ 58.30716684 115.2976154 20.11650907 0.91643344 199.49830585]\n",
320
- " [189.70973312 29.17579981 93.34984535 144.49503616 108.74375928]\n",
321
- " [ 66.61164501 167.19244399 139.38867947 52.12149803 23.92542262]\n",
322
- " [124.99918862 171.27254716 176.59560018 123.54288949 61.2720056 ]\n",
323
- " [ 62.94516036 112.18738057 157.45099897 43.03534539 192.60239645]\n",
324
- " [ 69.50587057 60.4803078 159.78661763 69.47100966 147.72643729]]\n"
325
- ]
326
- }
327
- ],
328
- "source": [
329
- "# rand 4 to 6\n",
330
- "import numpy as np\n",
331
- "n = 10\n",
332
- "m = 5\n",
333
- "a = np.random.rand(n, m) * 200\n",
334
- "print(a)"
335
- ]
336
- },
337
- {
338
- "cell_type": "code",
339
- "execution_count": 62,
340
- "metadata": {},
341
- "outputs": [
342
- {
343
- "name": "stdout",
344
- "output_type": "stream",
345
- "text": [
346
- "[20.24185503]\n"
347
- ]
348
- }
349
- ],
350
- "source": [
351
- "# open /data2/suhongyuan/flp/gurobi_result/2013_123.pkl\n",
352
- "import pickle\n",
353
- "with open(\"/data2/suhongyuan/flp/gurobi_result/2000_200.pkl\", \"rb\") as f:\n",
354
- " result = pickle.load(f)\n",
355
- " print(result)"
356
- ]
357
- },
358
- {
359
- "cell_type": "code",
360
- "execution_count": 95,
361
- "metadata": {},
362
- "outputs": [
363
- {
364
- "data": {
365
- "text/plain": [
366
- "[<matplotlib.lines.Line2D at 0x7f249bf083d0>]"
367
- ]
368
- },
369
- "execution_count": 95,
370
- "metadata": {},
371
- "output_type": "execute_result"
372
- },
373
- {
374
- "data": {
375
- "image/png": "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",
376
- "text/plain": [
377
- "<Figure size 640x480 with 1 Axes>"
378
- ]
379
- },
380
- "metadata": {},
381
- "output_type": "display_data"
382
- }
383
- ],
384
- "source": [
385
- "import pickle\n",
386
- "\n",
387
- "data_path1 = '/data2/suhongyuan/flp/output/dg-agent-rl-gnn-seed-1_1/best-models/eval_500_44_1.pkl'\n",
388
- "data_path2 = '/data2/suhongyuan/flp/output/dg-agent-rl-gnn-seed-1_1/best-models/eval_500_44_19.pkl'\n",
389
- "data_path3 = '/data2/suhongyuan/flp/output/dg-agent-rl-gnn-seed-1_1/best-models/eval_500_44_21.pkl'\n",
390
- "data1 = pickle.load(open(data_path1, 'rb'))\n",
391
- "data2 = pickle.load(open(data_path2, 'rb'))\n",
392
- "data3 = pickle.load(open(data_path3, 'rb'))\n",
393
- "# best_data[i] = max(data1[:i+1])\n",
394
- "# plot data123\n",
395
- "import matplotlib.pyplot as plt\n",
396
- "import numpy as np\n",
397
- "\n",
398
- "plt.plot(data1, label='1')\n",
399
- "plt.plot(data2, label='2')\n",
400
- "plt.plot(data3, label='3')"
401
- ]
402
- }
403
- ],
404
- "metadata": {
405
- "kernelspec": {
406
- "display_name": "torch-1.13-py310",
407
- "language": "python",
408
- "name": "python3"
409
- },
410
- "language_info": {
411
- "codemirror_mode": {
412
- "name": "ipython",
413
- "version": 3
414
- },
415
- "file_extension": ".py",
416
- "mimetype": "text/x-python",
417
- "name": "python",
418
- "nbconvert_exporter": "python",
419
- "pygments_lexer": "ipython3",
420
- "version": "3.10.12"
421
- }
422
- },
423
- "nbformat": 4,
424
- "nbformat_minor": 2
425
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
facility_location/train.py DELETED
@@ -1,274 +0,0 @@
1
- import os
2
- import setproctitle
3
-
4
- from absl import app, flags
5
- import time
6
- import random
7
- import pickle
8
- from typing import Union, Optional, Text
9
-
10
- import numpy as np
11
- import torch as th
12
-
13
- import sys
14
- import gymnasium
15
- sys.modules["gym"] = gymnasium
16
-
17
- from stable_baselines3.common.env_util import make_vec_env
18
- from stable_baselines3.common.vec_env import VecNormalize, VecEnvWrapper, DummyVecEnv
19
- from stable_baselines3.common.evaluation import evaluate_policy
20
- from stable_baselines3 import PPO
21
- from stable_baselines3.common.monitor import Monitor
22
- from stable_baselines3.common.callbacks import CallbackList, CheckpointCallback, EvalCallback
23
-
24
- from facility_location.env import PMPEnv, EvalPMPEnv
25
- from facility_location.utils.config import Config
26
- from facility_location.agent import MaskedFacilityLocationActorCriticPolicy
27
- from facility_location.utils.policy import get_policy_kwargs
28
- from utils import DictVecCheckNan, UpdateValEnv, UpdateValEnvAndStopTrainingOnNoModelImprovement, HParamCallback
29
-
30
- import warnings
31
- warnings.filterwarnings('ignore')
32
-
33
-
34
- flags.DEFINE_string('cfg', None, 'Configuration file.')
35
- flags.DEFINE_integer('global_seed', 0, 'Used in env and weight initialization, does not impact action sampling.')
36
- flags.DEFINE_bool('debug', False, 'Whether to use debug mode.')
37
- flags.DEFINE_string('root_dir', '/data2/suhongyuan/flp', 'Root directory for writing '
38
- 'logs/summaries/checkpoints.')
39
- flags.DEFINE_bool('tmp', False, 'Whether to use temporary storage.')
40
- flags.DEFINE_bool('save_ckpt', True, 'Whether to save checkpoints.')
41
- flags.DEFINE_bool('reset_num_timesteps', True, 'Whether to reset the current timestamp number.')
42
- flags.DEFINE_integer('save_freq', 10000, 'Save ckpt every save_freq steps.')
43
- flags.DEFINE_bool('validate', True, 'Whether to test on validation set during training.')
44
- flags.DEFINE_integer('val_freq', 5000, 'Test on validation set every val_freq steps.')
45
- flags.DEFINE_bool('early_stop', True, 'Whether to stop training if no improvements are made.')
46
- flags.DEFINE_integer('early_stop_patience', 10, 'Patience of early stop.')
47
- flags.DEFINE_integer('early_stop_min_num_vals', 50, 'Patience of early stop.')
48
- flags.DEFINE_enum('agent', 'rl-mlp', ['rl-mlp', 'rl-gnn', 'rl-agnn'], 'Agent type.')
49
- flags.DEFINE_integer('num_envs', 20, 'Number of environments for parallel training.')
50
- flags.DEFINE_float('lr', 3e-4, 'Learning rate.')
51
- flags.DEFINE_integer('steps_per_iteration', 5000, 'Number of timestamps per training iteration.')
52
- flags.DEFINE_integer('batch_size', 512, 'Mini-batch size.')
53
- flags.DEFINE_integer('optim_epochs_per_iteration', 10, 'Number of epochs for optimization per iteration.')
54
- flags.DEFINE_float('gamma', 0.99, 'Discount factor.')
55
- flags.DEFINE_float('gae_lambda', 0.95, 'Factor for trade-off of bias vs variance for Generalized Advantage Estimator.')
56
- flags.DEFINE_float('ent_coef', 0.01, 'Weight for entropy loss.')
57
- flags.DEFINE_float('vf_coef', 0.5, 'Weight for value loss.')
58
- flags.DEFINE_integer('train_steps', 1_000_000, 'Total number of training steps.')
59
- flags.DEFINE_bool('normalize_reward', True, 'Whether to normalize reward during training.')
60
- flags.DEFINE_string('device', 'cuda:3', 'gpu index.')
61
- FLAGS = flags.FLAGS
62
-
63
-
64
- def get_model(cfg: Config,
65
- env: Union[VecEnvWrapper, DummyVecEnv, EvalPMPEnv],
66
- training: bool = True,
67
- load_from_file: bool = False,
68
- ckpt_path: Text = None) -> PPO:
69
- policy_kwargs = get_policy_kwargs(cfg)
70
- tb_log_path = cfg.tb_log_path if training else None
71
- n_steps = max(FLAGS.steps_per_iteration // FLAGS.num_envs, 10) if training else 10
72
- if not load_from_file:
73
- model = PPO(MaskedFacilityLocationActorCriticPolicy,
74
- env,
75
- learning_rate=FLAGS.lr,
76
- n_steps=n_steps,
77
- batch_size=FLAGS.batch_size,
78
- n_epochs=FLAGS.optim_epochs_per_iteration,
79
- gamma=FLAGS.gamma,
80
- gae_lambda=FLAGS.gae_lambda,
81
- ent_coef=FLAGS.ent_coef,
82
- vf_coef=FLAGS.vf_coef,
83
- verbose=1,
84
- policy_kwargs=policy_kwargs,
85
- tensorboard_log=tb_log_path,
86
- device=FLAGS.device,
87
- )
88
- else:
89
- model = PPO.load(ckpt_path,
90
- env=env,
91
- learning_rate=FLAGS.lr,
92
- n_steps=n_steps,
93
- batch_size=FLAGS.batch_size,
94
- n_epochs=FLAGS.optim_epochs_per_iteration,
95
- gamma=FLAGS.gamma,
96
- gae_lambda=FLAGS.gae_lambda,
97
- ent_coef=FLAGS.ent_coef,
98
- vf_coef=FLAGS.vf_coef,
99
- verbose=1,
100
- tensorboard_log=tb_log_path)
101
- return model
102
-
103
-
104
- def get_best_model(cfg: Config) -> PPO:
105
- best_model_path = os.path.join(cfg.best_model_path, 'best_model.zip')
106
- model = PPO.load(best_model_path)
107
- return model
108
-
109
- def get_latest_model(cfg: Config) -> PPO:
110
- latest_model_path = os.path.join(cfg.latest_model_path, 'latest_model.zip')
111
- model = PPO.load(latest_model_path)
112
- return model
113
-
114
-
115
- def get_callbacks(cfg: Config) -> Optional[CallbackList]:
116
- callback_list = []
117
- hparam_callback = HParamCallback()
118
- callback_list.append(hparam_callback)
119
- if FLAGS.save_ckpt:
120
- save_freq = max(FLAGS.save_freq // FLAGS.num_envs, 1)
121
- ckpt_callback = CheckpointCallback(
122
- save_freq=save_freq,
123
- save_path=cfg.ckpt_save_path,
124
- name_prefix="rl_model",
125
- save_replay_buffer=False,
126
- save_vecnormalize=True,
127
- )
128
- callback_list.append(ckpt_callback)
129
- if FLAGS.validate:
130
- val_np = cfg.eval_specs['val_np']
131
- val_env = EvalPMPEnv(cfg, 'val', val_np)
132
- val_num_cases = val_env.get_eval_num_cases()
133
-
134
- val_env = Monitor(val_env)
135
- val_env = DummyVecEnv([lambda: val_env])
136
- val_env = VecNormalize(val_env, norm_obs=False, norm_reward=False)
137
- if FLAGS.debug:
138
- val_env = DictVecCheckNan(val_env, raise_exception=True)
139
-
140
- if FLAGS.early_stop:
141
- callback_after_eval = UpdateValEnvAndStopTrainingOnNoModelImprovement(
142
- val_env,
143
- max_no_improvement_evals=FLAGS.early_stop_patience,
144
- min_evals=FLAGS.early_stop_min_num_vals,
145
- )
146
- else:
147
- callback_after_eval = UpdateValEnv(val_env)
148
-
149
- val_freq = max(FLAGS.val_freq // FLAGS.num_envs, 1)
150
- val_callback = EvalCallback(
151
- val_env,
152
- callback_after_eval=callback_after_eval,
153
- best_model_save_path=cfg.best_model_path,
154
- n_eval_episodes=val_num_cases,
155
- log_path=cfg.best_model_path,
156
- eval_freq=val_freq,
157
- deterministic=True,
158
- render=False)
159
- callback_list.append(val_callback)
160
-
161
- if len(callback_list) == 0:
162
- callback_list = None
163
- else:
164
- callback_list = CallbackList(callback_list)
165
-
166
- return callback_list
167
-
168
-
169
- def calculate_gap(gurobi_obj, method_obj):
170
- method_obj = np.array(method_obj)
171
-
172
- gap = (method_obj - gurobi_obj) / gurobi_obj
173
- mean_gap = np.mean(gap)
174
- std_gap = np.std(gap)
175
-
176
- return mean_gap, std_gap
177
-
178
- def main(_):
179
- setproctitle.setproctitle('rl@suhy')
180
-
181
- th.manual_seed(FLAGS.global_seed)
182
- np.random.seed(FLAGS.global_seed)
183
- random.seed(FLAGS.global_seed)
184
-
185
- cfg = Config(FLAGS.cfg, FLAGS.global_seed, FLAGS.tmp, FLAGS.root_dir, FLAGS.agent, FLAGS.reset_num_timesteps)
186
-
187
- env = make_vec_env(PMPEnv, n_envs=FLAGS.num_envs, seed=FLAGS.global_seed, env_kwargs={'cfg': cfg})
188
- env = VecNormalize(env, norm_obs=False, norm_reward=FLAGS.normalize_reward)
189
-
190
- if FLAGS.debug:
191
- th.autograd.set_detect_anomaly(True)
192
- np.seterr(all='raise')
193
- env = DictVecCheckNan(env, raise_exception=True)
194
-
195
- if FLAGS.reset_num_timesteps:
196
- print(th.cuda.is_available())
197
- model = get_model(cfg, env)
198
- print(f'Creating new model.')
199
- else:
200
- latest_model_path = os.path.join(cfg.latest_model_path, 'latest_model')
201
- print(f'Loading model from {latest_model_path}')
202
- model = get_model(cfg, env, load_from_file=True, ckpt_path=latest_model_path)
203
- callback_list = get_callbacks(cfg)
204
- model.learn(
205
- total_timesteps=FLAGS.train_steps,
206
- callback=callback_list,
207
- tb_log_name=cfg.tb_log_name,
208
- reset_num_timesteps=FLAGS.reset_num_timesteps,
209
- progress_bar=True)
210
- latest_model_path = os.path.join(cfg.latest_model_path, 'latest_model.zip')
211
- model.save(latest_model_path)
212
-
213
- if cfg.eval_specs['region'] is None:
214
- eval_np = cfg.eval_specs['test_np']
215
- else:
216
- eval_path = './data/{}/pkl'.format(cfg.eval_specs['region'])
217
- files = os.listdir(eval_path)
218
- eval_np = []
219
-
220
- for f in files:
221
- eval_np.append(tuple(map(int, f.split('.')[0].split('_'))))
222
- eval_np = sorted(eval_np, key=lambda x: (x[0], x[1]))
223
-
224
- for (n, p) in eval_np:
225
- print(f'case ({n}, {p}):')
226
- eval_env = EvalPMPEnv(cfg, 'test', (n, p))
227
- eval_num_cases = eval_env.get_eval_num_cases()
228
-
229
- eval_env = Monitor(eval_env)
230
- eval_env = DummyVecEnv([lambda: eval_env])
231
- if FLAGS.debug:
232
- eval_env = DictVecCheckNan(eval_env, raise_exception=True)
233
- test_model = get_model(cfg, eval_env, training=False)
234
- trained_best_model = get_best_model(cfg)
235
- test_model.set_parameters(trained_best_model.get_parameters())
236
- start_time = time.time()
237
- episode_rewards, _ = evaluate_policy(test_model, eval_env, n_eval_episodes=eval_num_cases, return_episode_rewards=True)
238
- eval_time = time.time() - start_time
239
-
240
- gurobi_obj = pickle.load(open(f'gurobi_result/{n}_{p}.pkl', 'rb'))
241
- mean_gap, std_gap = calculate_gap(gurobi_obj, episode_rewards)
242
- print(f'\t mean gap: {mean_gap}')
243
- print(f'\t std gap: {std_gap}')
244
- print(f'\t time: {eval_time / eval_num_cases}')
245
-
246
- for (n, p) in eval_np:
247
- print(f'case ({n}, {p}):')
248
- eval_env = EvalPMPEnv(cfg, 'test', (n, p))
249
- eval_num_cases = eval_env.get_eval_num_cases()
250
-
251
- eval_env = Monitor(eval_env)
252
- eval_env = DummyVecEnv([lambda: eval_env])
253
- if FLAGS.debug:
254
- eval_env = DictVecCheckNan(eval_env, raise_exception=True)
255
- test_model = get_model(cfg, eval_env, training=False)
256
- trained_best_model = get_latest_model(cfg)
257
- test_model.set_parameters(trained_best_model.get_parameters())
258
- start_time = time.time()
259
- episode_rewards, _ = evaluate_policy(test_model, eval_env, n_eval_episodes=eval_num_cases, return_episode_rewards=True)
260
- eval_time = time.time() - start_time
261
-
262
- gurobi_obj = pickle.load(open(f'gurobi_result/{n}_{p}.pkl', 'rb'))
263
- mean_gap, std_gap = calculate_gap(gurobi_obj, episode_rewards)
264
- print(f'\t mean gap: {mean_gap}')
265
- print(f'\t std gap: {std_gap}')
266
- print(f'\t time: {eval_time / eval_num_cases}')
267
-
268
-
269
- if __name__ == '__main__':
270
- flags.mark_flags_as_required([
271
- 'cfg',
272
- 'global_seed'
273
- ])
274
- app.run(main)