|  | @ -69,6 +69,26 @@ class StateValue: | 
		
	
		
			
				|  |  |     ranking: float |  |  |     ranking: float | 
		
	
		
			
				|  |  |     choices: dict = field(default_factory=default_value) |  |  |     choices: dict = field(default_factory=default_value) | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
		
			
				|  |  |  |  |  | @dataclass(frozen=False) | 
		
	
		
			
				|  |  |  |  |  | class TestResult: | 
		
	
		
			
				|  |  |  |  |  |     init_check_pes_min: float | 
		
	
		
			
				|  |  |  |  |  |     init_check_pes_max: float | 
		
	
		
			
				|  |  |  |  |  |     init_check_pes_avg: float | 
		
	
		
			
				|  |  |  |  |  |     init_check_opt_min: float | 
		
	
		
			
				|  |  |  |  |  |     init_check_opt_max: float | 
		
	
		
			
				|  |  |  |  |  |     init_check_opt_avg: float | 
		
	
		
			
				|  |  |  |  |  |     def __str__(self): | 
		
	
		
			
				|  |  |  |  |  |         return f"""Test Result: | 
		
	
		
			
				|  |  |  |  |  |     init_check_pes_min: {self.init_check_pes_min} | 
		
	
		
			
				|  |  |  |  |  |     init_check_pes_max: {self.init_check_pes_max} | 
		
	
		
			
				|  |  |  |  |  |     init_check_pes_avg: {self.init_check_pes_avg} | 
		
	
		
			
				|  |  |  |  |  |     init_check_opt_min: {self.init_check_opt_min} | 
		
	
		
			
				|  |  |  |  |  |     init_check_opt_max: {self.init_check_opt_max} | 
		
	
		
			
				|  |  |  |  |  |     init_check_opt_avg: {self.init_check_opt_avg} | 
		
	
		
			
				|  |  |  |  |  | """ | 
		
	
		
			
				|  |  |  |  |  |     def csv(self, ws=" "): | 
		
	
		
			
				|  |  |  |  |  |         return f"{self.init_check_pes_min:0.04f}{ws}{self.init_check_pes_max:0.04f}{ws}{self.init_check_pes_avg:0.04f}{ws}{self.init_check_opt_min:0.04f}{ws}{self.init_check_opt_max:0.04f}{ws}{self.init_check_opt_avg:0.04f}" | 
		
	
		
			
				|  |  |  |  |  | 
 | 
		
	
		
			
				|  |  | def exec(command,verbose=True): |  |  | def exec(command,verbose=True): | 
		
	
		
			
				|  |  |     if verbose: print(f"Executing {command}") |  |  |     if verbose: print(f"Executing {command}") | 
		
	
		
			
				|  |  |     system(f"echo {command} >> list_of_exec") |  |  |     system(f"echo {command} >> list_of_exec") | 
		
	
	
		
			
				|  | @ -77,7 +97,7 @@ def exec(command,verbose=True): | 
		
	
		
			
				|  |  | num_tests_per_cluster = 50 |  |  | num_tests_per_cluster = 50 | 
		
	
		
			
				|  |  | factor_tests_per_cluster = 0.2 |  |  | factor_tests_per_cluster = 0.2 | 
		
	
		
			
				|  |  | num_ski_positions = 8 |  |  | num_ski_positions = 8 | 
		
	
		
			
				|  |  | num_velocities = 8 |  |  |  | 
		
	
		
			
				|  |  |  |  |  | num_velocities = 5 | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
		
			
				|  |  | def input_to_action(char): |  |  | def input_to_action(char): | 
		
	
		
			
				|  |  |     if char == "0": |  |  |     if char == "0": | 
		
	
	
		
			
				|  | @ -137,23 +157,41 @@ def run_single_test(ale, nn_wrapper, x,y,ski_position, velocity, duration=50): | 
		
	
		
			
				|  |  |             ale.act(input_to_action(str(action))) |  |  |             ale.act(input_to_action(str(action))) | 
		
	
		
			
				|  |  |         speed_list.append(ale.getRAM()[14]) |  |  |         speed_list.append(ale.getRAM()[14]) | 
		
	
		
			
				|  |  |         if len(speed_list) > 15 and sum(speed_list[-6:-1]) == 0: |  |  |         if len(speed_list) > 15 and sum(speed_list[-6:-1]) == 0: | 
		
	
		
			
				|  |  |             saveObservations(all_obs, Verdict.BAD, testDir) |  |  |  | 
		
	
		
			
				|  |  |  |  |  |             #saveObservations(all_obs, Verdict.BAD, testDir) | 
		
	
		
			
				|  |  |             return Verdict.BAD |  |  |             return Verdict.BAD | 
		
	
		
			
				|  |  |     saveObservations(all_obs, Verdict.GOOD, testDir) |  |  |  | 
		
	
		
			
				|  |  |  |  |  |     #saveObservations(all_obs, Verdict.GOOD, testDir) | 
		
	
		
			
				|  |  |     return Verdict.GOOD |  |  |     return Verdict.GOOD | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
		
			
				|  |  | def computeStateRanking(mdp_file, iteration): |  |  | def computeStateRanking(mdp_file, iteration): | 
		
	
		
			
				|  |  |     logger.info("Computing state ranking") |  |  |     logger.info("Computing state ranking") | 
		
	
		
			
				|  |  |     tic() |  |  |     tic() | 
		
	
		
			
				|  |  |  |  |  |     prop =  f"filter(min, Pmin=? [ G !(\"Hit_Tree\" | \"Hit_Gate\" | \"Unsafe\") ], !\"Hit_Tree\" & !\"Hit_Gate\" & !\"Unsafe\" );" | 
		
	
		
			
				|  |  |  |  |  |     prop += f"filter(max, Pmin=? [ G !(\"Hit_Tree\" | \"Hit_Gate\" | \"Unsafe\") ], !\"Hit_Tree\" & !\"Hit_Gate\" & !\"Unsafe\" );" | 
		
	
		
			
				|  |  |  |  |  |     prop += f"filter(avg, Pmin=? [ G !(\"Hit_Tree\" | \"Hit_Gate\" | \"Unsafe\") ], !\"Hit_Tree\" & !\"Hit_Gate\" & !\"Unsafe\" );" | 
		
	
		
			
				|  |  |  |  |  |     prop += f"filter(min, Pmax=? [ G !(\"Hit_Tree\" | \"Hit_Gate\" | \"Unsafe\") ], !\"Hit_Tree\" & !\"Hit_Gate\" & !\"Unsafe\" );" | 
		
	
		
			
				|  |  |  |  |  |     prop += f"filter(max, Pmax=? [ G !(\"Hit_Tree\" | \"Hit_Gate\" | \"Unsafe\") ], !\"Hit_Tree\" & !\"Hit_Gate\" & !\"Unsafe\" );" | 
		
	
		
			
				|  |  |  |  |  |     prop += f"filter(avg, Pmax=? [ G !(\"Hit_Tree\" | \"Hit_Gate\" | \"Unsafe\") ], !\"Hit_Tree\" & !\"Hit_Gate\" & !\"Unsafe\" );" | 
		
	
		
			
				|  |  |  |  |  |     prop += 'Rmax=? [C <= 200]' | 
		
	
		
			
				|  |  |  |  |  |     results = list() | 
		
	
		
			
				|  |  |     try: |  |  |     try: | 
		
	
		
			
				|  |  |         command = f"{tempest_binary} --prism {mdp_file} --buildchoicelab --buildstateval --build-all-labels --prop 'Rmax=? [C <= 1000]'" |  |  |  | 
		
	
		
			
				|  |  |         result = subprocess.run(command, shell=True, check=True) |  |  |  | 
		
	
		
			
				|  |  |         print(result) |  |  |  | 
		
	
		
			
				|  |  |     except Exception as e: |  |  |  | 
		
	
		
			
				|  |  |         print(e) |  |  |  | 
		
	
		
			
				|  |  |         sys.exit(-1) |  |  |  | 
		
	
		
			
				|  |  |     exec(f"mv action_ranking action_ranking_{iteration:03}") |  |  |  | 
		
	
		
			
				|  |  |  |  |  |         command = f"{tempest_binary} --prism {mdp_file} --buildchoicelab --buildstateval --build-all-labels --prop '{prop}'" | 
		
	
		
			
				|  |  |  |  |  |         output = subprocess.check_output(command, shell=True).decode("utf-8").split('\n') | 
		
	
		
			
				|  |  |  |  |  |         for line in output: | 
		
	
		
			
				|  |  |  |  |  |             print(line) | 
		
	
		
			
				|  |  |  |  |  |             if "Result" in line and not len(results) >= 6: | 
		
	
		
			
				|  |  |  |  |  |                 range_value = re.search(r"(.*:).*\[(-?\d+\.?\d*), (-?\d+\.?\d*)\].*", line) | 
		
	
		
			
				|  |  |  |  |  |                 if range_value: | 
		
	
		
			
				|  |  |  |  |  |                     results.append(float(range_value.group(2))) | 
		
	
		
			
				|  |  |  |  |  |                     results.append(float(range_value.group(3))) | 
		
	
		
			
				|  |  |  |  |  |                 else: | 
		
	
		
			
				|  |  |  |  |  |                     value = re.search(r"(.*:)(.*)", line) | 
		
	
		
			
				|  |  |  |  |  |                     results.append(float(value.group(2))) | 
		
	
		
			
				|  |  |  |  |  |         exec(f"mv action_ranking action_ranking_{iteration:03}") | 
		
	
		
			
				|  |  |  |  |  |     except subprocess.CalledProcessError as e: | 
		
	
		
			
				|  |  |  |  |  |         # todo die gracefully if ranking is uniform | 
		
	
		
			
				|  |  |  |  |  |         print(e.output) | 
		
	
		
			
				|  |  |     logger.info(f"Computing state ranking - DONE: took {toc()} seconds") |  |  |     logger.info(f"Computing state ranking - DONE: took {toc()} seconds") | 
		
	
		
			
				|  |  |  |  |  |     return TestResult(*tuple(results)) | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
		
			
				|  |  | def fillStateRanking(file_name, match=""): |  |  | def fillStateRanking(file_name, match=""): | 
		
	
		
			
				|  |  |     logger.info(f"Parsing state ranking, {file_name}") |  |  |     logger.info(f"Parsing state ranking, {file_name}") | 
		
	
	
		
			
				|  | @ -189,46 +227,17 @@ def fillStateRanking(file_name, match=""): | 
		
	
		
			
				|  |  | def createDisjunction(formulas): |  |  | def createDisjunction(formulas): | 
		
	
		
			
				|  |  |     return " | ".join(formulas) |  |  |     return " | ".join(formulas) | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
		
			
				|  |  | def clusterFormula(cluster): |  |  |  | 
		
	
		
			
				|  |  |     if len(cluster) == 0: return |  |  |  | 
		
	
		
			
				|  |  |     formulas = list() |  |  |  | 
		
	
		
			
				|  |  |     for state in cluster: |  |  |  | 
		
	
		
			
				|  |  |         formulas.append(f"(x={state[0].x} & y={state[0].y} & velocity={state[0].velocity} & ski_position={state[0].ski_position})") |  |  |  | 
		
	
		
			
				|  |  | 
 |  |  |  | 
		
	
		
			
				|  |  |     while len(formulas) > 1: |  |  |  | 
		
	
		
			
				|  |  |         formulas_tmp = [f"({formulas[i]} | {formulas[i+1]})" for i in range(0,len(formulas)//2)] |  |  |  | 
		
	
		
			
				|  |  |         if len(formulas) % 2 == 1: |  |  |  | 
		
	
		
			
				|  |  |             formulas_tmp.append(formulas[-1]) |  |  |  | 
		
	
		
			
				|  |  |         formulas = formulas_tmp |  |  |  | 
		
	
		
			
				|  |  | 
 |  |  |  | 
		
	
		
			
				|  |  |     return "(" + formulas[0] + ")" |  |  |  | 
		
	
		
			
				|  |  | 
 |  |  |  | 
		
	
		
			
				|  |  | def clusterFormulaXY(cluster): |  |  |  | 
		
	
		
			
				|  |  |     if len(cluster) == 0: return |  |  |  | 
		
	
		
			
				|  |  |     formulas = set() |  |  |  | 
		
	
		
			
				|  |  |     for state in cluster: |  |  |  | 
		
	
		
			
				|  |  |         formulas.add(f"(x={state[0].x} & y={state[0].y})") |  |  |  | 
		
	
		
			
				|  |  |     formulas = list(formulas) |  |  |  | 
		
	
		
			
				|  |  |     while len(formulas) > 1: |  |  |  | 
		
	
		
			
				|  |  |         formulas_tmp = [f"({formulas[i]} | {formulas[i+1]})" for i in range(0,len(formulas)//2)] |  |  |  | 
		
	
		
			
				|  |  |         if len(formulas) % 2 == 1: |  |  |  | 
		
	
		
			
				|  |  |             formulas_tmp.append(formulas[-1]) |  |  |  | 
		
	
		
			
				|  |  |         formulas = formulas_tmp |  |  |  | 
		
	
		
			
				|  |  | 
 |  |  |  | 
		
	
		
			
				|  |  |     return "(" + formulas[0] + ")" |  |  |  | 
		
	
		
			
				|  |  | 
 |  |  |  | 
		
	
		
			
				|  |  | def clusterFormulaTrimmed(cluster): |  |  |  | 
		
	
		
			
				|  |  |     formula = "" |  |  |  | 
		
	
		
			
				|  |  |     states = [(s[0].x,s[0].y, s[0].ski_position, s[0].velocity) for s in cluster] |  |  |  | 
		
	
		
			
				|  |  |  |  |  | def statesFormulaTrimmed(states): | 
		
	
		
			
				|  |  |  |  |  |     if len(states) == 0: return "false" | 
		
	
		
			
				|  |  |     #states = [(s[0].x,s[0].y, s[0].ski_position) for s in cluster] |  |  |     #states = [(s[0].x,s[0].y, s[0].ski_position) for s in cluster] | 
		
	
		
			
				|  |  |     skiPositionGroup = defaultdict(list) |  |  |     skiPositionGroup = defaultdict(list) | 
		
	
		
			
				|  |  |     for item in states: |  |  |     for item in states: | 
		
	
		
			
				|  |  |         skiPositionGroup[item[2]].append(item) |  |  |         skiPositionGroup[item[2]].append(item) | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
		
			
				|  |  |     #todo add velocity here |  |  |  | 
		
	
		
			
				|  |  |     firstVelocity = True |  |  |  | 
		
	
		
			
				|  |  |  |  |  |     formulas = list() | 
		
	
		
			
				|  |  |     for skiPosition, skiPos_group in skiPositionGroup.items(): |  |  |     for skiPosition, skiPos_group in skiPositionGroup.items(): | 
		
	
		
			
				|  |  |         formula += f"ski_position={skiPosition} & " |  |  |  | 
		
	
		
			
				|  |  |  |  |  |         formula = f"( ski_position={skiPosition} & " | 
		
	
		
			
				|  |  |  |  |  |         firstVelocity = True | 
		
	
		
			
				|  |  |         velocityGroup = defaultdict(list) |  |  |         velocityGroup = defaultdict(list) | 
		
	
		
			
				|  |  |         for item in skiPos_group: |  |  |         for item in skiPos_group: | 
		
	
		
			
				|  |  |             velocityGroup[item[3]].append(item) |  |  |             velocityGroup[item[3]].append(item) | 
		
	
	
		
			
				|  | @ -263,41 +272,25 @@ def clusterFormulaTrimmed(cluster): | 
		
	
		
			
				|  |  |                 formula += " | ".join(x_ranges) |  |  |                 formula += " | ".join(x_ranges) | 
		
	
		
			
				|  |  |                 formula += ") )" |  |  |                 formula += ") )" | 
		
	
		
			
				|  |  |             formula += ")" |  |  |             formula += ")" | 
		
	
		
			
				|  |  |         return formula |  |  |  | 
		
	
		
			
				|  |  | 
 |  |  |  | 
		
	
		
			
				|  |  | 
 |  |  |  | 
		
	
		
			
				|  |  | 
 |  |  |  | 
		
	
		
			
				|  |  | def createBalancedDisjunction(indices, name): |  |  |  | 
		
	
		
			
				|  |  |     #logger.info(f"Creating balanced disjunction for {len(indices)} ({indices}) formulas") |  |  |  | 
		
	
		
			
				|  |  |     if len(indices) == 0: |  |  |  | 
		
	
		
			
				|  |  |         return f"formula {name} = false;\n" |  |  |  | 
		
	
		
			
				|  |  |     else: |  |  |  | 
		
	
		
			
				|  |  |         while len(indices) > 1: |  |  |  | 
		
	
		
			
				|  |  |             indices_tmp = [f"({indices[i]} | {indices[i+1]})" for i in range(0,len(indices)//2)] |  |  |  | 
		
	
		
			
				|  |  |             if len(indices) % 2 == 1: |  |  |  | 
		
	
		
			
				|  |  |                 indices_tmp.append(indices[-1]) |  |  |  | 
		
	
		
			
				|  |  |             indices = indices_tmp |  |  |  | 
		
	
		
			
				|  |  |         disjunction = f"formula {name} = " + " ".join(indices) + ";\n" |  |  |  | 
		
	
		
			
				|  |  |     return disjunction |  |  |  | 
		
	
		
			
				|  |  | 
 |  |  |  | 
		
	
		
			
				|  |  | def createUnsafeFormula(clusters): |  |  |  | 
		
	
		
			
				|  |  |     label = "label \"Unsafe\" = Unsafe;\n" |  |  |  | 
		
	
		
			
				|  |  |     formulas = "" |  |  |  | 
		
	
		
			
				|  |  |     indices = list() |  |  |  | 
		
	
		
			
				|  |  |     for i, cluster in enumerate(clusters): |  |  |  | 
		
	
		
			
				|  |  |         formulas += f"formula Unsafe_{i} = {clusterFormulaXY(cluster)};\n" |  |  |  | 
		
	
		
			
				|  |  |         indices.append(f"Unsafe_{i}") |  |  |  | 
		
	
		
			
				|  |  |     return formulas + "\n" + createBalancedDisjunction(indices, "Unsafe") + label |  |  |  | 
		
	
		
			
				|  |  | 
 |  |  |  | 
		
	
		
			
				|  |  | def createSafeFormula(clusters): |  |  |  | 
		
	
		
			
				|  |  |     label = "label \"Safe\" = Safe;\n" |  |  |  | 
		
	
		
			
				|  |  |     formulas = "" |  |  |  | 
		
	
		
			
				|  |  |     indices = list() |  |  |  | 
		
	
		
			
				|  |  |     for i, cluster in enumerate(clusters): |  |  |  | 
		
	
		
			
				|  |  |         formulas += f"formula Safe_{i} = {clusterFormulaXY(cluster)};\n" |  |  |  | 
		
	
		
			
				|  |  |         indices.append(f"Safe_{i}") |  |  |  | 
		
	
		
			
				|  |  | 
 |  |  |  | 
		
	
		
			
				|  |  |     return formulas + "\n" + createBalancedDisjunction(indices, "Safe") + label |  |  |  | 
		
	
		
			
				|  |  |  |  |  |         formula += ")" | 
		
	
		
			
				|  |  |  |  |  |         formulas.append(formula) | 
		
	
		
			
				|  |  |  |  |  |     return createBalancedDisjunction(formulas) | 
		
	
		
			
				|  |  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |  | # https://stackoverflow.com/questions/5389507/iterating-over-every-two-elements-in-a-list | 
		
	
		
			
				|  |  |  |  |  | def pairwise(iterable): | 
		
	
		
			
				|  |  |  |  |  |     "s -> (s0, s1), (s2, s3), (s4, s5), ..." | 
		
	
		
			
				|  |  |  |  |  |     a = iter(iterable) | 
		
	
		
			
				|  |  |  |  |  |     return zip(a, a) | 
		
	
		
			
				|  |  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |  | def createBalancedDisjunction(formulas): | 
		
	
		
			
				|  |  |  |  |  |     if len(formulas) == 0: | 
		
	
		
			
				|  |  |  |  |  |         return "false" | 
		
	
		
			
				|  |  |  |  |  |     while len(formulas) > 1: | 
		
	
		
			
				|  |  |  |  |  |         formulas_tmp = [f"({f} | {g})"  for f,g in pairwise(formulas)] | 
		
	
		
			
				|  |  |  |  |  |         if len(formulas) % 2 == 1: | 
		
	
		
			
				|  |  |  |  |  |             formulas_tmp.append(formulas[-1]) | 
		
	
		
			
				|  |  |  |  |  |         formulas = formulas_tmp | 
		
	
		
			
				|  |  |  |  |  |     return " ".join(formulas) | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
		
			
				|  |  | def updatePrismFile(newFile, iteration, safeStates, unsafeStates): |  |  | def updatePrismFile(newFile, iteration, safeStates, unsafeStates): | 
		
	
		
			
				|  |  |     logger.info("Creating next prism file") |  |  |     logger.info("Creating next prism file") | 
		
	
	
		
			
				|  | @ -306,8 +299,10 @@ def updatePrismFile(newFile, iteration, safeStates, unsafeStates): | 
		
	
		
			
				|  |  |     newFile = f"{newFile}_{iteration:03}.prism" |  |  |     newFile = f"{newFile}_{iteration:03}.prism" | 
		
	
		
			
				|  |  |     exec(f"cp {initFile} {newFile}", verbose=False) |  |  |     exec(f"cp {initFile} {newFile}", verbose=False) | 
		
	
		
			
				|  |  |     with open(newFile, "a") as prism: |  |  |     with open(newFile, "a") as prism: | 
		
	
		
			
				|  |  |         prism.write(createSafeFormula(safeStates)) |  |  |  | 
		
	
		
			
				|  |  |         prism.write(createUnsafeFormula(unsafeStates)) |  |  |  | 
		
	
		
			
				|  |  |  |  |  |         prism.write(f"formula Safe = {statesFormulaTrimmed(safeStates)};\n") | 
		
	
		
			
				|  |  |  |  |  |         prism.write(f"formula Unsafe = {statesFormulaTrimmed(unsafeStates)};\n") | 
		
	
		
			
				|  |  |  |  |  |         prism.write(f"label \"Safe\" = Safe;\n") | 
		
	
		
			
				|  |  |  |  |  |         prism.write(f"label \"Unsafe\" = Unsafe;\n") | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
		
			
				|  |  |     logger.info(f"Creating next prism file - DONE: took {toc()} seconds") |  |  |     logger.info(f"Creating next prism file - DONE: took {toc()} seconds") | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
	
		
			
				|  | @ -331,7 +326,7 @@ x = 70 | 
		
	
		
			
				|  |  | nn_wrapper = SampleFactoryNNQueryWrapper() |  |  | nn_wrapper = SampleFactoryNNQueryWrapper() | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
		
			
				|  |  | experiment_id = int(time.time()) |  |  | experiment_id = int(time.time()) | 
		
	
		
			
				|  |  | init_mdp = "safety" |  |  |  | 
		
	
		
			
				|  |  |  |  |  | init_mdp = "velocity_safety" | 
		
	
		
			
				|  |  | exec(f"mkdir -p images/testing_{experiment_id}", verbose=False) |  |  | exec(f"mkdir -p images/testing_{experiment_id}", verbose=False) | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
		
			
				|  |  | markerSize = 1 |  |  | markerSize = 1 | 
		
	
	
		
			
				|  | @ -340,27 +335,46 @@ imagesDir = f"images/testing_{experiment_id}" | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
		
			
				|  |  | def drawOntoSkiPosImage(states, color, target_prefix="cluster_", alpha_factor=1.0): |  |  | def drawOntoSkiPosImage(states, color, target_prefix="cluster_", alpha_factor=1.0): | 
		
	
		
			
				|  |  |     #markerList = {ski_position:list() for ski_position in range(1,num_ski_positions + 1)} |  |  |     #markerList = {ski_position:list() for ski_position in range(1,num_ski_positions + 1)} | 
		
	
		
			
				|  |  |     markerList = {(ski_position, velocity):list() for velocity in range(0, num_velocities + 1) for ski_position in range(1,num_ski_positions + 1)} |  |  |  | 
		
	
		
			
				|  |  |  |  |  |     markerList = {(ski_position, velocity):list() for velocity in range(0, num_velocities) for ski_position in range(1,num_ski_positions + 1)} | 
		
	
		
			
				|  |  |  |  |  |     images = dict() | 
		
	
		
			
				|  |  |  |  |  |     mergedImages = dict() | 
		
	
		
			
				|  |  |  |  |  |     for ski_position in range(1, num_ski_positions + 1): | 
		
	
		
			
				|  |  |  |  |  |         for velocity in range(0,num_velocities): | 
		
	
		
			
				|  |  |  |  |  |             images[(ski_position, velocity)] = cv2.imread(f"{imagesDir}/{target_prefix}_{ski_position:02}_{velocity:02}_individual.png") | 
		
	
		
			
				|  |  |  |  |  |         mergedImages[ski_position] = cv2.imread(f"{imagesDir}/{target_prefix}_{ski_position:02}_individual.png") | 
		
	
		
			
				|  |  |     for state in states: |  |  |     for state in states: | 
		
	
		
			
				|  |  |         s = state[0] |  |  |         s = state[0] | 
		
	
		
			
				|  |  |         #marker = f"-fill 'rgba({color}, {alpha_factor * state[1].ranking})' -draw 'rectangle {s.x-markerSize},{s.y-markerSize} {s.x+markerSize},{s.y+markerSize} '" |  |  |         #marker = f"-fill 'rgba({color}, {alpha_factor * state[1].ranking})' -draw 'rectangle {s.x-markerSize},{s.y-markerSize} {s.x+markerSize},{s.y+markerSize} '" | 
		
	
		
			
				|  |  |         marker = f"-fill 'rgba({color}, {alpha_factor * state[1].ranking})' -draw 'point {s.x},{s.y} '" |  |  |  | 
		
	
		
			
				|  |  |  |  |  |         #marker = f"-fill 'rgba({color}, {alpha_factor * state[1].ranking})' -draw 'point {s.x},{s.y} '" | 
		
	
		
			
				|  |  |  |  |  |         marker = [color, alpha_factor * state[1].ranking, (s.x-markerSize, s.y-markerSize), (s.x+markerSize, s.y+markerSize)] | 
		
	
		
			
				|  |  |         markerList[(s.ski_position, s.velocity)].append(marker) |  |  |         markerList[(s.ski_position, s.velocity)].append(marker) | 
		
	
		
			
				|  |  |     for (pos, vel), marker in markerList.items(): |  |  |     for (pos, vel), marker in markerList.items(): | 
		
	
		
			
				|  |  |         command = f"convert {imagesDir}/{target_prefix}_{pos:02}_{vel:02}_individual.png {' '.join(marker)} {imagesDir}/{target_prefix}_{pos:02}_{vel:02}_individual.png" |  |  |  | 
		
	
		
			
				|  |  |         exec(command, verbose=False) |  |  |  | 
		
	
		
			
				|  |  |  |  |  |         #command = f"convert {imagesDir}/{target_prefix}_{pos:02}_{vel:02}_individual.png {' '.join(marker)} {imagesDir}/{target_prefix}_{pos:02}_{vel:02}_individual.png" | 
		
	
		
			
				|  |  |  |  |  |         #exec(command, verbose=False) | 
		
	
		
			
				|  |  |  |  |  |         if len(marker) == 0: continue | 
		
	
		
			
				|  |  |  |  |  |         for m in marker: | 
		
	
		
			
				|  |  |  |  |  |             images[(pos,vel)] = cv2.rectangle(images[(pos,vel)], m[2], m[3], m[0], cv2.FILLED) | 
		
	
		
			
				|  |  |  |  |  |             mergedImages[pos] = cv2.rectangle(mergedImages[pos], m[2], m[3], m[0], cv2.FILLED) | 
		
	
		
			
				|  |  |  |  |  |     for (ski_position, velocity), image in images.items(): | 
		
	
		
			
				|  |  |  |  |  |         cv2.imwrite(f"{imagesDir}/{target_prefix}_{ski_position:02}_{velocity:02}_individual.png", image) | 
		
	
		
			
				|  |  |  |  |  |     for ski_position, image in mergedImages.items(): | 
		
	
		
			
				|  |  |  |  |  |         cv2.imwrite(f"{imagesDir}/{target_prefix}_{ski_position:02}_individual.png", image) | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
		
			
				|  |  | def concatImages(prefix, iteration): |  |  | def concatImages(prefix, iteration): | 
		
	
		
			
				|  |  |     images = [f"{imagesDir}/{prefix}_{pos:02}_{vel:02}_individual.png" for vel in range(0,num_velocities+1) for pos in range(1,num_ski_positions+1) ] |  |  |  | 
		
	
		
			
				|  |  |     for vel in range(0, num_velocities + 1): |  |  |  | 
		
	
		
			
				|  |  |  |  |  |     logger.info(f"Concatenating images") | 
		
	
		
			
				|  |  |  |  |  |     images = [f"{imagesDir}/{prefix}_{pos:02}_{vel:02}_individual.png" for vel in range(0,num_velocities) for pos in range(1,num_ski_positions+1)] | 
		
	
		
			
				|  |  |  |  |  |     mergedImages = [f"{imagesDir}/{prefix}_{pos:02}_individual.png" for pos in range(1,num_ski_positions+1)] | 
		
	
		
			
				|  |  |  |  |  |     for vel in range(0, num_velocities): | 
		
	
		
			
				|  |  |         for pos in range(1, num_ski_positions + 1): |  |  |         for pos in range(1, num_ski_positions + 1): | 
		
	
		
			
				|  |  |             command =  f"convert {imagesDir}/{prefix}_{pos:02}_{vel:02}_individual.png " |  |  |             command =  f"convert {imagesDir}/{prefix}_{pos:02}_{vel:02}_individual.png " | 
		
	
		
			
				|  |  |             command += f"-pointsize 10 -gravity NorthEast -annotate +8+0 'p{pos:02}v{vel:02}' " |  |  |             command += f"-pointsize 10 -gravity NorthEast -annotate +8+0 'p{pos:02}v{vel:02}' " | 
		
	
		
			
				|  |  |             command += f"{imagesDir}/{prefix}_{pos:02}_{vel:02}_individual.png" |  |  |             command += f"{imagesDir}/{prefix}_{pos:02}_{vel:02}_individual.png" | 
		
	
		
			
				|  |  |             exec(command, verbose=False) |  |  |             exec(command, verbose=False) | 
		
	
		
			
				|  |  |     exec(f"montage {' '.join(images)} -geometry +0+0 -tile 8x9 {imagesDir}/{prefix}_{iteration}.png", verbose=False) |  |  |     exec(f"montage {' '.join(images)} -geometry +0+0 -tile 8x9 {imagesDir}/{prefix}_{iteration}.png", verbose=False) | 
		
	
		
			
				|  |  |  |  |  |     exec(f"montage {' '.join(mergedImages)} -geometry +0+0 -tile 8x9 {imagesDir}/{prefix}_{iteration}_merged.png", verbose=False) | 
		
	
		
			
				|  |  |     #exec(f"sxiv {imagesDir}/{prefix}_{iteration}.png&", verbose=False) |  |  |     #exec(f"sxiv {imagesDir}/{prefix}_{iteration}.png&", verbose=False) | 
		
	
		
			
				|  |  |  |  |  |     logger.info(f"Concatenating images - DONE") | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
		
			
				|  |  | def drawStatesOntoTiledImage(states, color, target, source="images/1_full_scaled_down.png", alpha_factor=1.0): |  |  | def drawStatesOntoTiledImage(states, color, target, source="images/1_full_scaled_down.png", alpha_factor=1.0): | 
		
	
		
			
				|  |  |     """ |  |  |     """ | 
		
	
	
		
			
				|  | @ -380,33 +394,35 @@ def drawStatesOntoTiledImage(states, color, target, source="images/1_full_scaled | 
		
	
		
			
				|  |  |     """ |  |  |     """ | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
		
			
				|  |  | def drawClusters(clusterDict, target, iteration, alpha_factor=1.0): |  |  | def drawClusters(clusterDict, target, iteration, alpha_factor=1.0): | 
		
	
		
			
				|  |  |     logger.info(f"Drawing clusters") |  |  |  | 
		
	
		
			
				|  |  |  |  |  |     logger.info(f"Drawing {len(clusterDict)} clusters") | 
		
	
		
			
				|  |  |     tic() |  |  |     tic() | 
		
	
		
			
				|  |  |     for velocity in range(0, num_velocities + 1): |  |  |  | 
		
	
		
			
				|  |  |         for ski_position in range(1, num_ski_positions + 1): |  |  |  | 
		
	
		
			
				|  |  |             source = "images/1_full_scaled_down.png" |  |  |  | 
		
	
		
			
				|  |  |             exec(f"cp {source} {imagesDir}/{target}_{ski_position:02}_{velocity:02}_individual.png", verbose=False) |  |  |  | 
		
	
		
			
				|  |  |  |  |  |     #for velocity in range(0, num_velocities): | 
		
	
		
			
				|  |  |  |  |  |     #    for ski_position in range(1, num_ski_positions + 1): | 
		
	
		
			
				|  |  |  |  |  |     #        source = "images/1_full_scaled_down.png" | 
		
	
		
			
				|  |  |  |  |  |     #        exec(f"cp {source} {imagesDir}/{target}_{ski_position:02}_{velocity:02}_individual.png", verbose=False) | 
		
	
		
			
				|  |  |     for _, clusterStates in clusterDict.items(): |  |  |     for _, clusterStates in clusterDict.items(): | 
		
	
		
			
				|  |  |         color = f"{np.random.choice(range(256))}, {np.random.choice(range(256))}, {np.random.choice(range(256))}" |  |  |  | 
		
	
		
			
				|  |  |  |  |  |         color = (np.random.choice(range(256)), np.random.choice(range(256)), np.random.choice(range(256))) | 
		
	
		
			
				|  |  |  |  |  |         color = (int(color[0]), int(color[1]), int(color[2])) | 
		
	
		
			
				|  |  |         drawOntoSkiPosImage(clusterStates, color, target, alpha_factor=alpha_factor) |  |  |         drawOntoSkiPosImage(clusterStates, color, target, alpha_factor=alpha_factor) | 
		
	
		
			
				|  |  |     concatImages(target, iteration) |  |  |     concatImages(target, iteration) | 
		
	
		
			
				|  |  |     logger.info(f"Drawing clusters - DONE: took {toc()} seconds") |  |  |  | 
		
	
		
			
				|  |  |  |  |  |     logger.info(f"Drawing {len(clusterDict)} clusters - DONE: took {toc()} seconds") | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
		
			
				|  |  | def drawResult(clusterDict, target, iteration): |  |  | def drawResult(clusterDict, target, iteration): | 
		
	
		
			
				|  |  |     logger.info(f"Drawing clusters") |  |  |  | 
		
	
		
			
				|  |  |     for velocity in range(0,num_velocities+1): |  |  |  | 
		
	
		
			
				|  |  |         for ski_position in range(1, num_ski_positions + 1): |  |  |  | 
		
	
		
			
				|  |  |             source = "images/1_full_scaled_down.png" |  |  |  | 
		
	
		
			
				|  |  |             exec(f"cp {source} {imagesDir}/{target}_{ski_position:02}_{velocity:02}_individual.png", verbose=False) |  |  |  | 
		
	
		
			
				|  |  |  |  |  |     logger.info(f"Drawing {len(clusterDict)} results") | 
		
	
		
			
				|  |  |  |  |  |     #for velocity in range(0,num_velocities): | 
		
	
		
			
				|  |  |  |  |  |     #    for ski_position in range(1, num_ski_positions + 1): | 
		
	
		
			
				|  |  |  |  |  |     #        source = "images/1_full_scaled_down.png" | 
		
	
		
			
				|  |  |  |  |  |     #        exec(f"cp {source} {imagesDir}/{target}_{ski_position:02}_{velocity:02}_individual.png", verbose=False) | 
		
	
		
			
				|  |  |     for _, (clusterStates, result) in clusterDict.items(): |  |  |     for _, (clusterStates, result) in clusterDict.items(): | 
		
	
		
			
				|  |  |         color = "100,100,100" |  |  |  | 
		
	
		
			
				|  |  |  |  |  |         # opencv wants BGR | 
		
	
		
			
				|  |  |  |  |  |         color = (100,100,100) | 
		
	
		
			
				|  |  |         if result == Verdict.GOOD: |  |  |         if result == Verdict.GOOD: | 
		
	
		
			
				|  |  |             color = "0,200,0" |  |  |  | 
		
	
		
			
				|  |  |  |  |  |             color = (0,200,0) | 
		
	
		
			
				|  |  |         elif result == Verdict.BAD: |  |  |         elif result == Verdict.BAD: | 
		
	
		
			
				|  |  |             color = "200,0,0" |  |  |  | 
		
	
		
			
				|  |  |  |  |  |             color = (0,0,200) | 
		
	
		
			
				|  |  |         drawOntoSkiPosImage(clusterStates, color, target, alpha_factor=0.7) |  |  |         drawOntoSkiPosImage(clusterStates, color, target, alpha_factor=0.7) | 
		
	
		
			
				|  |  |     concatImages(target, iteration) |  |  |     concatImages(target, iteration) | 
		
	
		
			
				|  |  |     logger.info(f"Drawing clusters - DONE: took {toc()} seconds") |  |  |  | 
		
	
		
			
				|  |  |  |  |  |     logger.info(f"Drawing {len(clusterDict)} results - DONE: took {toc()} seconds") | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
		
			
				|  |  | def _init_logger(): |  |  | def _init_logger(): | 
		
	
		
			
				|  |  |     logger = logging.getLogger('main') |  |  |     logger = logging.getLogger('main') | 
		
	
	
		
			
				|  | @ -422,14 +438,20 @@ def clusterImportantStates(ranking, iteration): | 
		
	
		
			
				|  |  |     states = [[s[0].x,s[0].y, s[0].ski_position * 20, s[0].velocity * 20, s[1].ranking] for s in ranking] |  |  |     states = [[s[0].x,s[0].y, s[0].ski_position * 20, s[0].velocity * 20, s[1].ranking] for s in ranking] | 
		
	
		
			
				|  |  |     #states = [[s[0].x,s[0].y, s[0].ski_position * 30, s[1].ranking] for s in ranking] |  |  |     #states = [[s[0].x,s[0].y, s[0].ski_position * 30, s[1].ranking] for s in ranking] | 
		
	
		
			
				|  |  |     #kmeans = KMeans(n_clusters, random_state=0, n_init="auto").fit(states) |  |  |     #kmeans = KMeans(n_clusters, random_state=0, n_init="auto").fit(states) | 
		
	
		
			
				|  |  |     dbscan = DBSCAN(eps=15).fit(states) |  |  |  | 
		
	
		
			
				|  |  |  |  |  |     dbscan = DBSCAN(eps=5).fit(states) | 
		
	
		
			
				|  |  |     labels = dbscan.labels_ |  |  |     labels = dbscan.labels_ | 
		
	
		
			
				|  |  |     n_clusters = len(set(labels)) - (1 if -1 in labels else 0) |  |  |     n_clusters = len(set(labels)) - (1 if -1 in labels else 0) | 
		
	
		
			
				|  |  |     logger.info(f"Starting to cluster {len(ranking)} states into clusters - DONE: took {toc()} seconds with {n_clusters} cluster") |  |  |     logger.info(f"Starting to cluster {len(ranking)} states into clusters - DONE: took {toc()} seconds with {n_clusters} cluster") | 
		
	
		
			
				|  |  |     clusterDict = {i : list() for i in range(0,n_clusters)} |  |  |     clusterDict = {i : list() for i in range(0,n_clusters)} | 
		
	
		
			
				|  |  |  |  |  |     strayStates = list() | 
		
	
		
			
				|  |  |     for i, state in enumerate(ranking): |  |  |     for i, state in enumerate(ranking): | 
		
	
		
			
				|  |  |         if labels[i] == -1: continue |  |  |  | 
		
	
		
			
				|  |  |  |  |  |         if labels[i] == -1: | 
		
	
		
			
				|  |  |  |  |  |             clusterDict[n_clusters + len(strayStates) + 1] = list() | 
		
	
		
			
				|  |  |  |  |  |             clusterDict[n_clusters + len(strayStates) + 1].append(state) | 
		
	
		
			
				|  |  |  |  |  |             strayStates.append(state) | 
		
	
		
			
				|  |  |  |  |  |             continue | 
		
	
		
			
				|  |  |         clusterDict[labels[i]].append(state) |  |  |         clusterDict[labels[i]].append(state) | 
		
	
		
			
				|  |  |  |  |  |     if len(strayStates) > 0: logger.warning(f"{len(strayStates)} stray states with label -1") | 
		
	
		
			
				|  |  |     drawClusters(clusterDict, f"clusters", iteration) |  |  |     drawClusters(clusterDict, f"clusters", iteration) | 
		
	
		
			
				|  |  |     return clusterDict |  |  |     return clusterDict | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
	
		
			
				|  | @ -440,21 +462,42 @@ if __name__ == '__main__': | 
		
	
		
			
				|  |  |     n_clusters = 40 |  |  |     n_clusters = 40 | 
		
	
		
			
				|  |  |     testAll = False |  |  |     testAll = False | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
		
			
				|  |  |     safeStates = list() |  |  |  | 
		
	
		
			
				|  |  |     unsafeStates = list() |  |  |  | 
		
	
		
			
				|  |  |  |  |  |     source = "images/1_full_scaled_down.png" | 
		
	
		
			
				|  |  |  |  |  |     for ski_position in range(1, num_ski_positions + 1): | 
		
	
		
			
				|  |  |  |  |  |         for velocity in range(0,num_velocities): | 
		
	
		
			
				|  |  |  |  |  |             exec(f"cp {source} {imagesDir}/clusters_{ski_position:02}_{velocity:02}_individual.png", verbose=False) | 
		
	
		
			
				|  |  |  |  |  |             exec(f"cp {source} {imagesDir}/result_{ski_position:02}_{velocity:02}_individual.png", verbose=False) | 
		
	
		
			
				|  |  |  |  |  |         exec(f"cp {source} {imagesDir}/clusters_{ski_position:02}_individual.png", verbose=False) | 
		
	
		
			
				|  |  |  |  |  |         exec(f"cp {source} {imagesDir}/result_{ski_position:02}_individual.png", verbose=False) | 
		
	
		
			
				|  |  |  |  |  |     safeStates = set() | 
		
	
		
			
				|  |  |  |  |  |     unsafeStates = set() | 
		
	
		
			
				|  |  |     iteration = 0 |  |  |     iteration = 0 | 
		
	
		
			
				|  |  |  |  |  |     results = list() | 
		
	
		
			
				|  |  |  |  |  |     eps = 0.1 | 
		
	
		
			
				|  |  |     while True: |  |  |     while True: | 
		
	
		
			
				|  |  |         updatePrismFile(init_mdp, iteration, safeStates, unsafeStates) |  |  |         updatePrismFile(init_mdp, iteration, safeStates, unsafeStates) | 
		
	
		
			
				|  |  |         computeStateRanking(f"{init_mdp}_{iteration:03}.prism", iteration) |  |  |  | 
		
	
		
			
				|  |  |  |  |  |         modelCheckingResult = computeStateRanking(f"{init_mdp}_{iteration:03}.prism", iteration) | 
		
	
		
			
				|  |  |  |  |  |         results.append(modelCheckingResult) | 
		
	
		
			
				|  |  |  |  |  |         logger.info(f"Model Checking Result: {modelCheckingResult}") | 
		
	
		
			
				|  |  |  |  |  |         if abs(modelCheckingResult.init_check_pes_avg - modelCheckingResult.init_check_opt_avg) < eps: | 
		
	
		
			
				|  |  |  |  |  |             logger.info(f"Absolute difference between average estimates is below eps = {eps}... finishing!") | 
		
	
		
			
				|  |  |  |  |  |             break | 
		
	
		
			
				|  |  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |  | 
 | 
		
	
		
			
				|  |  |         ranking = fillStateRanking(f"action_ranking_{iteration:03}") |  |  |         ranking = fillStateRanking(f"action_ranking_{iteration:03}") | 
		
	
		
			
				|  |  |         sorted_ranking = sorted( (x for x in ranking.items() if x[1].ranking > 0.1), key=lambda x: x[1].ranking) |  |  |         sorted_ranking = sorted( (x for x in ranking.items() if x[1].ranking > 0.1), key=lambda x: x[1].ranking) | 
		
	
		
			
				|  |  |         clusters = clusterImportantStates(sorted_ranking, iteration) |  |  |  | 
		
	
		
			
				|  |  |  |  |  |         try: | 
		
	
		
			
				|  |  |  |  |  |             clusters = clusterImportantStates(sorted_ranking, iteration) | 
		
	
		
			
				|  |  |  |  |  |         except Exception as e: | 
		
	
		
			
				|  |  |  |  |  |             print(e) | 
		
	
		
			
				|  |  |  |  |  |             break | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
		
			
				|  |  |         if testAll: failingPerCluster = {i: list() for i in range(0, n_clusters)} |  |  |         if testAll: failingPerCluster = {i: list() for i in range(0, n_clusters)} | 
		
	
		
			
				|  |  |         clusterResult = dict() |  |  |         clusterResult = dict() | 
		
	
		
			
				|  |  |  |  |  |         logger.info(f"Running tests") | 
		
	
		
			
				|  |  |  |  |  |         tic() | 
		
	
		
			
				|  |  |         for id, cluster in clusters.items(): |  |  |         for id, cluster in clusters.items(): | 
		
	
		
			
				|  |  |             num_tests = int(factor_tests_per_cluster * len(cluster)) |  |  |             num_tests = int(factor_tests_per_cluster * len(cluster)) | 
		
	
		
			
				|  |  |             num_tests = 1 |  |  |  | 
		
	
		
			
				|  |  |             #logger.info(f"Testing {num_tests} states (from {len(cluster)} states) from cluster {id}") |  |  |             #logger.info(f"Testing {num_tests} states (from {len(cluster)} states) from cluster {id}") | 
		
	
		
			
				|  |  |             randomStates = np.random.choice(len(cluster), num_tests, replace=False) |  |  |             randomStates = np.random.choice(len(cluster), num_tests, replace=False) | 
		
	
		
			
				|  |  |             randomStates = [cluster[i] for i in randomStates] |  |  |             randomStates = [cluster[i] for i in randomStates] | 
		
	
	
		
			
				|  | @ -467,22 +510,22 @@ if __name__ == '__main__': | 
		
	
		
			
				|  |  |                 velocity = state[0].velocity |  |  |                 velocity = state[0].velocity | 
		
	
		
			
				|  |  |                 #result = run_single_test(ale,nn_wrapper,x,y,ski_pos, duration=50) |  |  |                 #result = run_single_test(ale,nn_wrapper,x,y,ski_pos, duration=50) | 
		
	
		
			
				|  |  |                 result = run_single_test(ale,nn_wrapper,x,y,ski_pos, velocity, duration=50) |  |  |                 result = run_single_test(ale,nn_wrapper,x,y,ski_pos, velocity, duration=50) | 
		
	
		
			
				|  |  |                 result = Verdict.BAD # TODO REMOVE ME!!!!!!!!!!!!!! |  |  |  | 
		
	
		
			
				|  |  |                 if result == Verdict.BAD: |  |  |                 if result == Verdict.BAD: | 
		
	
		
			
				|  |  |                     if testAll: |  |  |                     if testAll: | 
		
	
		
			
				|  |  |                         failingPerCluster[id].append(state) |  |  |                         failingPerCluster[id].append(state) | 
		
	
		
			
				|  |  |                     else: |  |  |                     else: | 
		
	
		
			
				|  |  |                         clusterResult[id] = (cluster, Verdict.BAD) |  |  |                         clusterResult[id] = (cluster, Verdict.BAD) | 
		
	
		
			
				|  |  |                         verdictGood = False |  |  |                         verdictGood = False | 
		
	
		
			
				|  |  |                         unsafeStates.append(cluster) |  |  |  | 
		
	
		
			
				|  |  |  |  |  |                         unsafeStates.update([(s[0].x,s[0].y, s[0].ski_position, s[0].velocity) for s in cluster]) | 
		
	
		
			
				|  |  |                         break |  |  |                         break | 
		
	
		
			
				|  |  |             if verdictGood: |  |  |             if verdictGood: | 
		
	
		
			
				|  |  |                 clusterResult[id] = (cluster, Verdict.GOOD) |  |  |                 clusterResult[id] = (cluster, Verdict.GOOD) | 
		
	
		
			
				|  |  |                 safeStates.append(cluster) |  |  |  | 
		
	
		
			
				|  |  |         logger.info(f"Iteration: {iteration:03} -\tSafe Results : {sum([len(c) for c in safeStates])} -\tUnsafe Results:{sum([len(c) for c in unsafeStates])}") |  |  |  | 
		
	
		
			
				|  |  |  |  |  |                 safeStates.update([(s[0].x,s[0].y, s[0].ski_position, s[0].velocity) for s in cluster]) | 
		
	
		
			
				|  |  |  |  |  |         logger.info(f"Iteration: {iteration:03}\t-\tSafe Results : {len(safeStates)}\t-\tUnsafe Results:{len(unsafeStates)}") | 
		
	
		
			
				|  |  |         if testAll: drawClusters(failingPerCluster, f"failing", iteration) |  |  |         if testAll: drawClusters(failingPerCluster, f"failing", iteration) | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
		
			
				|  |  |         #drawResult(clusterResult, "result", iteration) |  |  |  | 
		
	
		
			
				|  |  |  |  |  |         drawResult(clusterResult, "result", iteration) | 
		
	
		
			
				|  |  |         iteration += 1 |  |  |         iteration += 1 | 
		
	
		
			
				|  |  | 
 |  |  | 
 | 
		
	
		
			
				|  |  | 
 |  |  |  | 
		
	
		
			
				|  |  |  |  |  |     for result in results: | 
		
	
		
			
				|  |  |  |  |  |         print(result.csv()) |