Skip to content

Commit b981092

Browse files
committed
Preparing compose config
1 parent 5c0ad6b commit b981092

File tree

6 files changed

+953
-235
lines changed

6 files changed

+953
-235
lines changed

alg/NSGA_II/conf/experiment_improved_random_key.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ defaults:
22
- experiment: basic
33

44
experiment:
5-
repetitions: 1
5+
repetitions: 10
66
n_iterations: 150
7-
problem_names: ["ft20"] #["ft06", "ft10", "ft20", "abz7", "abz8", "abz9", "la28", "la40"]
7+
problem_names: ["ft06", "ft10", "ft20", "abz7", "abz8", "abz9", "la28", "la40"]
88
experiment_id: 2
99
dump_population: False
1010

compose.yaml

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,18 @@ services:
1515
# cpuset: "1"
1616
# command: ./run_position_experiment.bash
1717

18-
qga_hash_jssp:
19-
build:
18+
#qga_hash_jssp:
19+
# build:
20+
# context: .
21+
# volumes:
22+
# - ./logdata_2:/QGA_JSSP/logdata_2
23+
# cpuset: "2"
24+
# command: ./run_hash_based.bash
25+
26+
qga_improv_rnadom_key:
27+
build:
2028
context: .
21-
volumes:
22-
- ./logdata_2:/QGA_JSSP/logdata_2
23-
cpuset: "2"
24-
command: ./run_hash_based.bash
29+
volumes:
30+
- ./logdata_2:/QGA_JSSP/logdata_2
31+
cpuset: "2"
32+
command: ./run_hash_based.bash

lab/alternative_rotation.ipynb

Lines changed: 730 additions & 220 deletions
Large diffs are not rendered by default.

qga_lib/individual.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,88 @@ def rotate(self,
10701070
# Converge more significant bits
10711071
self.binary_chromosome[0, :cutoffpoint] = np.logical_not(b.x[:cutoffpoint])
10721072
self.binary_chromosome[1, :cutoffpoint] = b.x[:cutoffpoint]
1073+
1074+
1075+
class EnhancedQuantumRandomKeyIndividual(Individual):
1076+
1077+
def __init__(self, n_jobs: int, n_machines: int, individual_cfg: DictConfig, time_log: bool=False) -> None:
1078+
self.individual_cfg = individual_cfg
1079+
self.n_machines = n_machines
1080+
self.n_jobs = n_jobs
1081+
self.time_log = time_log
1082+
self.start_std = self.individual_cfg.start_std
1083+
self.base_permutation = np.repeat(np.arange(self.n_jobs), self.n_machines)
1084+
self.initialize_individual()
1085+
self.convert_permutation()
1086+
1087+
def initialize_individual(self):
1088+
self.positions = np.random.randint(0, self.n_jobs, size=self.n_jobs*self.n_machines)
1089+
self.standard_deviations = np.ones(shape=self.n_jobs*self.n_machines) * self.start_std
1090+
self.random_keys = np.zeros_like(self.positions)
1091+
1092+
def periodic_triangular_function_vectorized(self, x: np.ndarray, j: int) -> np.ndarray:
1093+
y = lambda x, offset, sign: sign*(x - offset)
1094+
y_res = np.empty_like(x)
1095+
1096+
x1 = x[(np.floor(x) // j) % 2 == 0]
1097+
x2 = x[(np.floor(x) // j) % 2 > 0]
1098+
1099+
y_res[(np.floor(x) // j) % 2 == 0] = y(x1, (np.floor(x1) // j)*j, 1)
1100+
y_res[(np.floor(x) // j) % 2 > 0] = y(x2, ((np.floor(x2) // j)*j)+j, -1)
1101+
1102+
return np.floor(y_res)
1103+
1104+
def measure(self):
1105+
self.random_keys = self.periodic_triangular_function_vectorized(np.round(np.random.normal(0, self.standard_deviations) + self.positions).astype(int), self.n_jobs-1)
1106+
1107+
def convert_permutation(self):
1108+
self.permutation = self.base_permutation[np.argsort(self.random_keys)]
1109+
1110+
def rotate(self,
1111+
b: object,
1112+
c: int,
1113+
c_tot: int,
1114+
n_groups: int,
1115+
cur_group : int,
1116+
rotation_angles: str="[0, 0, 1, 0, 0, 0, 1, 0]",
1117+
std_deltas: str="[-0.3, -0.1, -0.3, -0.1]"):
1118+
1119+
# transfer all angles into interval [0, j]
1120+
raw_rotation_angles = eval(rotation_angles)
1121+
if len(raw_rotation_angles) != 8:
1122+
raise ValueError("Please specify rotation angle array of length 8.")
1123+
1124+
raw_std_deltas = eval(std_deltas)
1125+
if len(raw_std_deltas) != 4:
1126+
raise ValueError("Please specify std delta array of length 4.")
1127+
1128+
rotation_angles = np.array(raw_rotation_angles)
1129+
std_deltas = np.array(raw_std_deltas)
1130+
1131+
b_keys = b.random_keys
1132+
x_keys = self.random_keys
1133+
x_filtered = self.positions
1134+
# Check if the two angles are within the variance
1135+
equal_cases_overshoot = np.logical_and(b_keys == x_keys, (b_keys - x_keys) <= 0)
1136+
equal_cases_undershoot = np.logical_and(b_keys == x_keys, (b_keys - x_keys) >= 0)
1137+
unequal_cases_overshoot = np.logical_and(b_keys != x_keys, (b_keys - x_keys) < 0)
1138+
unequal_cases_undershoot = np.logical_and(b_keys != x_keys, (b_keys - x_keys) > 0)
1139+
1140+
x_filtered[equal_cases_overshoot] += rotation_angles[0] #np.random.uniform(0.01, 0.05)
1141+
x_filtered[unequal_cases_overshoot] -= rotation_angles[2] #np.random.uniform(0.01, 0.05)
1142+
x_filtered[equal_cases_undershoot] -= rotation_angles[4] #np.random.uniform(0.01, 0.05)
1143+
x_filtered[unequal_cases_undershoot] += rotation_angles[6] #np.random.uniform(0.01, 0.05)
1144+
# Contribute to std convergence
1145+
self.standard_deviations[equal_cases_overshoot] = np.abs(self.standard_deviations[equal_cases_overshoot] + std_deltas[0])
1146+
self.standard_deviations[unequal_cases_overshoot] = np.abs(self.standard_deviations[unequal_cases_overshoot] + std_deltas[1])
1147+
self.standard_deviations[equal_cases_undershoot] = np.abs(self.standard_deviations[equal_cases_undershoot] + std_deltas[2])
1148+
self.standard_deviations[unequal_cases_undershoot] = np.abs(self.standard_deviations[unequal_cases_undershoot] + std_deltas[3])
1149+
# If the standard deviaiton has become negative, make sure it is
1150+
#self.standard_deviations[self.standard_deviations < 0] = 0
1151+
# Handle values outside the supported range
1152+
self.positions = np.abs(x_filtered) % self.n_jobs
1153+
return self.permutation
1154+
10731155

10741156

10751157
if __name__=="__main__":

qga_lib/nsga_population.py

Lines changed: 114 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
from operations import Operation
2525
from schedules import Schedule
26-
from individual import Individual, PermutationChromosome
26+
from individual import Individual, PermutationChromosome, EnhancedQuantumRandomKeyIndividual
2727
from or_benchmark import BenchmarkCollection
2828
import matplotlib.pyplot as plt
2929
from PIL import Image
@@ -294,7 +294,7 @@ def non_dominated_sorting(self):
294294
# For each individual go through the rest to find the best possible individual (that dominates all solutions)
295295
dominated = False
296296
for j in range(cur_start, len(self.R)):
297-
# swap if J dominates the
297+
# swap if J dominates the
298298
cur_comparison = np.array(self.R[j].cur_fitness) <= np.array(self.R[i].cur_fitness)
299299
if (i != j) and (cur_comparison).all():
300300
# Break the loop if the individual i is dominated -> means that the individual does not belong to the current front
@@ -309,9 +309,6 @@ def non_dominated_sorting(self):
309309
# Move swap position one step to the right
310310
cur_swap_index += 1
311311

312-
# What happens when all solutions are dominated.
313-
# What happens if only one individual is in a front
314-
315312
# Close the front
316313
if cur_start == cur_swap_index:
317314
# There is only one front in the set,
@@ -323,8 +320,9 @@ def non_dominated_sorting(self):
323320
cur_start = cur_swap_index
324321

325322
# Add the last front
326-
if self.front_start_index[-1] < self.N:
327-
self.front_start_index.append(self.N)
323+
#if self.front_start_index[-1] < self.N:
324+
self.front_start_index.append(cur_start)
325+
328326

329327

330328
def get_front_range(self, i: int) -> List:
@@ -898,4 +896,113 @@ def execute_quantum_update(self, c: int, c_tot: int):
898896
temp_front_indexes.append(j)
899897

900898

899+
front_indexes = temp_front_indexes
900+
901+
902+
class EnhancedQMEAPopulation(Population):
903+
def __init__(self,
904+
N: int,
905+
reset_fraction: float,
906+
decoding_method: str,
907+
n_jobs: int,
908+
n_machines: int,
909+
jssp_problem: np.ndarray,
910+
individual_cfg: DictConfig,
911+
rotation_angles: str,
912+
std_deltas: str,
913+
group_partitions: int,
914+
activate_schedule: bool=False,
915+
time_log: bool=False,
916+
individual_type="EnhancedQuantumRandomKeyIndividual"
917+
):
918+
super().__init__(N, decoding_method, n_jobs, n_machines, jssp_problem, activate_schedule, time_log)
919+
self.individual_type = individual_type
920+
self.reset_fraction = reset_fraction
921+
self.rotation_angles = rotation_angles
922+
self.std_deltas = std_deltas
923+
self.Individual_cfg = individual_cfg
924+
self.group_partitions = group_partitions
925+
self.initialize_population(time_log)
926+
self.evaluate_fitness()
927+
928+
def evaluate_fitness(self):
929+
for cur_chromosome in self.R:
930+
# make measurement
931+
cur_chromosome.measure()
932+
# Convert bit string to operation based representation
933+
cur_chromosome.convert_permutation()
934+
# Create schedule to evaluate fitness
935+
cur_chromosome.create_schedule(
936+
self.decoding_method,
937+
self.jssp_problem,
938+
self.activate_schedule
939+
)
940+
# Fintess values are available as self.R[i].schedule.max_completion_time
941+
942+
def initialize_population(self, time_log: bool=False):
943+
"""Method for populating the population with individuals. Generates random qubit chromosomes.
944+
945+
Parameters
946+
----------
947+
time_log : bool, optional
948+
Logging durations of different components in representation, by default False
949+
"""
950+
# P is index 0 - N-1, while Q is index N - 2N
951+
cur_individual_type = eval(self.individual_type)
952+
self.R = np.empty(2*self.N, dtype=cur_individual_type)
953+
for i in range(len(self.R)):
954+
self.R[i] = cur_individual_type(self.n_jobs, self.n_machines, self.Individual_cfg, time_log=time_log)
955+
956+
def execute_quantum_update(self, c: int, c_tot: int):
957+
"""This method is used to perform recombination for the QMEA algorithm
958+
959+
Parameters
960+
----------
961+
c : int
962+
The current generation number
963+
c_tot : int
964+
The total generation to run
965+
966+
Raises
967+
------
968+
Exception
969+
The groups of solutions should contains more solutions than 0.
970+
"""
971+
population_size = self.N*2*(1-self.reset_fraction)
972+
# Divide the parents into equal groups
973+
S = int(np.floor(population_size/self.group_partitions))
974+
if S <= 0:
975+
raise Exception("The groups of solutions should contains more solutions than 0. Please adjust the n_groups parameter.")
976+
977+
group = np.arange(1, self.group_partitions, 1)*S
978+
for g in group:
979+
for s in range(S):
980+
# For each solution in the best group, use it to rotate the solution
981+
# Solutions in other groups are given by S*group + s
982+
self.R[g + s].rotate(self.R[s], c=c, c_tot=c_tot, n_groups=self.group_partitions, cur_group=g, rotation_angles=self.rotation_angles, std_deltas=self.std_deltas)
983+
984+
# Reset remaining solutions
985+
reset_index = int(self.N*2*(1-self.reset_fraction))
986+
for remaining in self.R[reset_index:]:
987+
remaining.initialize_individual()
988+
989+
# Set the genes of the best group
990+
for s in range(S):
991+
self.R[s].positions = self.R[s].random_keys
992+
self.R[s].standard_deviations = np.zeros_like(self.R[s].random_keys)
993+
994+
# Reset individuals that are duplicated
995+
cur_range = self.get_front_range(0)
996+
front_indexes = np.arange(cur_range[0], cur_range[1]).tolist()
997+
while len(front_indexes) > 0:
998+
cur_makespan, cur_flow = self.R[front_indexes[0]].cur_fitness
999+
temp_front_indexes = []
1000+
for j in range(1, len(front_indexes)):
1001+
comp_makespan, comp_flow = self.R[front_indexes[j]].cur_fitness
1002+
if cur_makespan == comp_makespan and cur_flow == comp_flow:
1003+
# Reset the duplicate to initial positions
1004+
self.R[front_indexes[j]].initialize_individual()
1005+
temp_front_indexes.append(j)
1006+
1007+
9011008
front_indexes = temp_front_indexes

run_improved_random_key.bash

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/bash
2+
3+
ls -la /QGA_JSSP/logdata_2
4+
5+
#python ./alg/NSGA_II/run_experiment.py -cn experiment_qga_position.yaml
6+
7+
for pop_size in 50
8+
do
9+
echo 'Running with N=$pop_size'
10+
python ./alg/NSGA_II/run_experiment.py -cn experiment_improved_random_key.yaml "qmea_enhanced_random_key={pop_object : {N : $pop_size}}"
11+
done

0 commit comments

Comments
 (0)