Spaces:
Runtime error
Runtime error
randommm
commited on
Commit
·
5d537f3
1
Parent(s):
0dce8af
update
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- facility_location/__pycache__/__init__.cpython-310.pyc +0 -0
- facility_location/__pycache__/__init__.cpython-37.pyc +0 -0
- facility_location/__pycache__/__init__.cpython-39.pyc +0 -0
- facility_location/__pycache__/eval.cpython-310.pyc +0 -0
- facility_location/__pycache__/eval.cpython-39.pyc +0 -0
- facility_location/__pycache__/multi_eval.cpython-310.pyc +0 -0
- facility_location/__pycache__/multi_eval.cpython-39.pyc +0 -0
- facility_location/__pycache__/train.cpython-310.pyc +0 -0
- facility_location/__pycache__/train.cpython-37.pyc +0 -0
- facility_location/__pycache__/train.cpython-39.pyc +0 -0
- facility_location/agent/__pycache__/__init__.cpython-39.pyc +0 -0
- facility_location/agent/__pycache__/features_extractor.cpython-39.pyc +0 -0
- facility_location/agent/__pycache__/policy.cpython-39.pyc +0 -0
- facility_location/agent/__pycache__/solver.cpython-39.pyc +0 -0
- facility_location/agent/ga.py +0 -86
- facility_location/agent/heuristic.py +0 -72
- facility_location/agent/metaheuristic.py +0 -218
- facility_location/agent/tests/ga.ipynb +0 -0
- facility_location/agent/tests/solver.ipynb +0 -142
- facility_location/cfg/2-nearest.yaml +0 -61
- facility_location/cfg/3-nearest.yaml +0 -63
- facility_location/cfg/NY.yaml +0 -65
- facility_location/cfg/dg.yaml +0 -63
- facility_location/cfg/gainloss.yaml +0 -63
- facility_location/cfg/multi.yaml +0 -69
- facility_location/cfg/plot.yaml +4 -4
- facility_location/cfg/popstar.yaml +0 -63
- facility_location/cfg/scale1.yaml +0 -63
- facility_location/cfg/scale5.yaml +0 -63
- facility_location/cfg/tabu0.yaml +0 -63
- facility_location/cfg/tabu5.yaml +0 -63
- facility_location/cfg/uniform.yaml +0 -63
- facility_location/cfg/uniform_debug.yaml +0 -64
- facility_location/env/__pycache__/__init__.cpython-39.pyc +0 -0
- facility_location/env/__pycache__/facility_location_client.cpython-310.pyc +0 -0
- facility_location/env/__pycache__/facility_location_client.cpython-39.pyc +0 -0
- facility_location/env/__pycache__/obs_extractor.cpython-310.pyc +0 -0
- facility_location/env/__pycache__/obs_extractor.cpython-39.pyc +0 -0
- facility_location/env/__pycache__/pmp.cpython-310.pyc +0 -0
- facility_location/env/__pycache__/pmp.cpython-39.pyc +0 -0
- facility_location/env/facility_location_client.py +44 -51
- facility_location/env/obs_extractor.py +1 -19
- facility_location/env/tests/p-median.ipynb +0 -0
- facility_location/env/tests/render.ipynb +0 -0
- facility_location/env/utils/env_test.ipynb +0 -0
- facility_location/eval.py +0 -234
- facility_location/multi_eval.py +15 -126
- facility_location/solutions.pkl +3 -0
- facility_location/test.ipynb +0 -425
- 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 |
-
|
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:
|
9 |
-
tabu_time:
|
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
|
|
|
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 |
-
|
83 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
191 |
-
|
|
|
|
|
|
|
|
|
192 |
self._drop_time[old_facility] = t
|
193 |
self._add_time[new_facility] = t
|
194 |
self._t = t
|
195 |
-
self.
|
|
|
|
|
|
|
|
|
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
|
74 |
-
|
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 |
-
|
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 |
-
|
159 |
-
|
160 |
-
random.seed(
|
161 |
-
|
162 |
-
|
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 |
-
|
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)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|