|
9 | 9 |
|
10 | 10 | class Helper: |
11 | 11 |
|
| 12 | + def summary(self, |
| 13 | + line_length=70, |
| 14 | + fill_character=" ", |
| 15 | + line_character="-", |
| 16 | + line_character2="=", |
| 17 | + columns_equal_len=False, |
| 18 | + print_step_parameters=True, |
| 19 | + print_parameters_summary=True): |
| 20 | + """ |
| 21 | + The summary() method prints a summary of the PyGAD lifecycle in a Keras style. |
| 22 | + The parameters are: |
| 23 | + line_length: An integer representing the length of the single line in characters. |
| 24 | + fill_character: A character to fill the lines. |
| 25 | + line_character: A character for creating a line separator. |
| 26 | + line_character2: A secondary character to create a line separator. |
| 27 | + columns_equal_len: The table rows are split into equal-sized columns or split subjective to the width needed. |
| 28 | + print_step_parameters: Whether to print extra parameters about each step inside the step. If print_step_parameters=False and print_parameters_summary=True, then the parameters of each step are printed at the end of the table. |
| 29 | + print_parameters_summary: Whether to print parameters summary at the end of the table. If print_step_parameters=False, then the parameters of each step are printed at the end of the table too. |
| 30 | + """ |
| 31 | + |
| 32 | + summary_output = "" |
| 33 | + |
| 34 | + def fill_message(msg, line_length=line_length, fill_character=fill_character): |
| 35 | + num_spaces = int((line_length - len(msg))/2) |
| 36 | + num_spaces = int(num_spaces / len(fill_character)) |
| 37 | + msg = "{spaces}{msg}{spaces}".format( |
| 38 | + msg=msg, spaces=fill_character * num_spaces) |
| 39 | + return msg |
| 40 | + |
| 41 | + def line_separator(line_length=line_length, line_character=line_character): |
| 42 | + num_characters = int(line_length / len(line_character)) |
| 43 | + return line_character * num_characters |
| 44 | + |
| 45 | + def create_row(columns, line_length=line_length, fill_character=fill_character, split_percentages=None): |
| 46 | + filled_columns = [] |
| 47 | + if split_percentages is None: |
| 48 | + split_percentages = [int(100/len(columns))] * 3 |
| 49 | + columns_lengths = [int((split_percentages[idx] * line_length) / 100) |
| 50 | + for idx in range(len(split_percentages))] |
| 51 | + for column_idx, column in enumerate(columns): |
| 52 | + current_column_length = len(column) |
| 53 | + extra_characters = columns_lengths[column_idx] - \ |
| 54 | + current_column_length |
| 55 | + filled_column = column + fill_character * extra_characters |
| 56 | + filled_columns.append(filled_column) |
| 57 | + |
| 58 | + return "".join(filled_columns) |
| 59 | + |
| 60 | + def print_parent_selection_params(): |
| 61 | + nonlocal summary_output |
| 62 | + m = f"Number of Parents: {self.num_parents_mating}" |
| 63 | + self.logger.info(m) |
| 64 | + summary_output = summary_output + m + "\n" |
| 65 | + if self.parent_selection_type == "tournament": |
| 66 | + m = f"K Tournament: {self.K_tournament}" |
| 67 | + self.logger.info(m) |
| 68 | + summary_output = summary_output + m + "\n" |
| 69 | + |
| 70 | + def print_fitness_params(): |
| 71 | + nonlocal summary_output |
| 72 | + if not self.fitness_batch_size is None: |
| 73 | + m = f"Fitness batch size: {self.fitness_batch_size}" |
| 74 | + self.logger.info(m) |
| 75 | + summary_output = summary_output + m + "\n" |
| 76 | + |
| 77 | + def print_crossover_params(): |
| 78 | + nonlocal summary_output |
| 79 | + if not self.crossover_probability is None: |
| 80 | + m = f"Crossover probability: {self.crossover_probability}" |
| 81 | + self.logger.info(m) |
| 82 | + summary_output = summary_output + m + "\n" |
| 83 | + |
| 84 | + def print_mutation_params(): |
| 85 | + nonlocal summary_output |
| 86 | + if not self.mutation_probability is None: |
| 87 | + m = f"Mutation Probability: {self.mutation_probability}" |
| 88 | + self.logger.info(m) |
| 89 | + summary_output = summary_output + m + "\n" |
| 90 | + if self.mutation_percent_genes == "default": |
| 91 | + m = f"Mutation Percentage: {self.mutation_percent_genes}" |
| 92 | + self.logger.info(m) |
| 93 | + summary_output = summary_output + m + "\n" |
| 94 | + # Number of mutation genes is already showed above. |
| 95 | + m = f"Mutation Genes: {self.mutation_num_genes}" |
| 96 | + self.logger.info(m) |
| 97 | + summary_output = summary_output + m + "\n" |
| 98 | + m = f"Random Mutation Range: ({self.random_mutation_min_val}, {self.random_mutation_max_val})" |
| 99 | + self.logger.info(m) |
| 100 | + summary_output = summary_output + m + "\n" |
| 101 | + if not self.gene_space is None: |
| 102 | + m = f"Gene Space: {self.gene_space}" |
| 103 | + self.logger.info(m) |
| 104 | + summary_output = summary_output + m + "\n" |
| 105 | + m = f"Mutation by Replacement: {self.mutation_by_replacement}" |
| 106 | + self.logger.info(m) |
| 107 | + summary_output = summary_output + m + "\n" |
| 108 | + m = f"Allow Duplicated Genes: {self.allow_duplicate_genes}" |
| 109 | + self.logger.info(m) |
| 110 | + summary_output = summary_output + m + "\n" |
| 111 | + |
| 112 | + def print_on_generation_params(): |
| 113 | + nonlocal summary_output |
| 114 | + if not self.stop_criteria is None: |
| 115 | + m = f"Stop Criteria: {self.stop_criteria}" |
| 116 | + self.logger.info(m) |
| 117 | + summary_output = summary_output + m + "\n" |
| 118 | + |
| 119 | + def print_params_summary(): |
| 120 | + nonlocal summary_output |
| 121 | + m = f"Population Size: ({self.sol_per_pop}, {self.num_genes})" |
| 122 | + self.logger.info(m) |
| 123 | + summary_output = summary_output + m + "\n" |
| 124 | + m = f"Number of Generations: {self.num_generations}" |
| 125 | + self.logger.info(m) |
| 126 | + summary_output = summary_output + m + "\n" |
| 127 | + m = f"Initial Population Range: ({self.init_range_low}, {self.init_range_high})" |
| 128 | + self.logger.info(m) |
| 129 | + summary_output = summary_output + m + "\n" |
| 130 | + |
| 131 | + if not print_step_parameters: |
| 132 | + print_fitness_params() |
| 133 | + |
| 134 | + if not print_step_parameters: |
| 135 | + print_parent_selection_params() |
| 136 | + |
| 137 | + if self.keep_elitism != 0: |
| 138 | + m = f"Keep Elitism: {self.keep_elitism}" |
| 139 | + self.logger.info(m) |
| 140 | + summary_output = summary_output + m + "\n" |
| 141 | + else: |
| 142 | + m = f"Keep Parents: {self.keep_parents}" |
| 143 | + self.logger.info(m) |
| 144 | + summary_output = summary_output + m + "\n" |
| 145 | + m = f"Gene DType: {self.gene_type}" |
| 146 | + self.logger.info(m) |
| 147 | + summary_output = summary_output + m + "\n" |
| 148 | + |
| 149 | + if not print_step_parameters: |
| 150 | + print_crossover_params() |
| 151 | + |
| 152 | + if not print_step_parameters: |
| 153 | + print_mutation_params() |
| 154 | + |
| 155 | + if not print_step_parameters: |
| 156 | + print_on_generation_params() |
| 157 | + |
| 158 | + if not self.parallel_processing is None: |
| 159 | + m = f"Parallel Processing: {self.parallel_processing}" |
| 160 | + self.logger.info(m) |
| 161 | + summary_output = summary_output + m + "\n" |
| 162 | + if not self.random_seed is None: |
| 163 | + m = f"Random Seed: {self.random_seed}" |
| 164 | + self.logger.info(m) |
| 165 | + summary_output = summary_output + m + "\n" |
| 166 | + m = f"Save Best Solutions: {self.save_best_solutions}" |
| 167 | + self.logger.info(m) |
| 168 | + summary_output = summary_output + m + "\n" |
| 169 | + m = f"Save Solutions: {self.save_solutions}" |
| 170 | + self.logger.info(m) |
| 171 | + summary_output = summary_output + m + "\n" |
| 172 | + |
| 173 | + m = line_separator(line_character=line_character) |
| 174 | + self.logger.info(m) |
| 175 | + summary_output = summary_output + m + "\n" |
| 176 | + m = fill_message("PyGAD Lifecycle") |
| 177 | + self.logger.info(m) |
| 178 | + summary_output = summary_output + m + "\n" |
| 179 | + m = line_separator(line_character=line_character2) |
| 180 | + self.logger.info(m) |
| 181 | + summary_output = summary_output + m + "\n" |
| 182 | + |
| 183 | + lifecycle_steps = ["on_start()", "Fitness Function", "On Fitness", "Parent Selection", "On Parents", |
| 184 | + "Crossover", "On Crossover", "Mutation", "On Mutation", "On Generation", "On Stop"] |
| 185 | + lifecycle_functions = [self.on_start, self.fitness_func, self.on_fitness, self.select_parents, self.on_parents, |
| 186 | + self.crossover, self.on_crossover, self.mutation, self.on_mutation, self.on_generation, self.on_stop] |
| 187 | + lifecycle_functions = [getattr( |
| 188 | + lifecycle_func, '__name__', "None") for lifecycle_func in lifecycle_functions] |
| 189 | + lifecycle_functions = [lifecycle_func + "()" if lifecycle_func != |
| 190 | + "None" else "None" for lifecycle_func in lifecycle_functions] |
| 191 | + lifecycle_output = ["None", "(1)", "None", f"({self.num_parents_mating}, {self.num_genes})", "None", |
| 192 | + f"({self.num_parents_mating}, {self.num_genes})", "None", f"({self.num_parents_mating}, {self.num_genes})", "None", "None", "None"] |
| 193 | + lifecycle_step_parameters = [None, print_fitness_params, None, print_parent_selection_params, None, |
| 194 | + print_crossover_params, None, print_mutation_params, None, print_on_generation_params, None] |
| 195 | + |
| 196 | + if not columns_equal_len: |
| 197 | + max_lengthes = [max(list(map(len, lifecycle_steps))), max( |
| 198 | + list(map(len, lifecycle_functions))), max(list(map(len, lifecycle_output)))] |
| 199 | + split_percentages = [ |
| 200 | + int((column_len / sum(max_lengthes)) * 100) for column_len in max_lengthes] |
| 201 | + else: |
| 202 | + split_percentages = None |
| 203 | + |
| 204 | + header_columns = ["Step", "Handler", "Output Shape"] |
| 205 | + header_row = create_row( |
| 206 | + header_columns, split_percentages=split_percentages) |
| 207 | + m = header_row |
| 208 | + self.logger.info(m) |
| 209 | + summary_output = summary_output + m + "\n" |
| 210 | + m = line_separator(line_character=line_character2) |
| 211 | + self.logger.info(m) |
| 212 | + summary_output = summary_output + m + "\n" |
| 213 | + |
| 214 | + for lifecycle_idx in range(len(lifecycle_steps)): |
| 215 | + lifecycle_column = [lifecycle_steps[lifecycle_idx], |
| 216 | + lifecycle_functions[lifecycle_idx], lifecycle_output[lifecycle_idx]] |
| 217 | + if lifecycle_column[1] == "None": |
| 218 | + continue |
| 219 | + lifecycle_row = create_row( |
| 220 | + lifecycle_column, split_percentages=split_percentages) |
| 221 | + m = lifecycle_row |
| 222 | + self.logger.info(m) |
| 223 | + summary_output = summary_output + m + "\n" |
| 224 | + if print_step_parameters: |
| 225 | + if not lifecycle_step_parameters[lifecycle_idx] is None: |
| 226 | + lifecycle_step_parameters[lifecycle_idx]() |
| 227 | + m = line_separator(line_character=line_character) |
| 228 | + self.logger.info(m) |
| 229 | + summary_output = summary_output + m + "\n" |
| 230 | + |
| 231 | + m = line_separator(line_character=line_character2) |
| 232 | + self.logger.info(m) |
| 233 | + summary_output = summary_output + m + "\n" |
| 234 | + if print_parameters_summary: |
| 235 | + print_params_summary() |
| 236 | + m = line_separator(line_character=line_character2) |
| 237 | + self.logger.info(m) |
| 238 | + summary_output = summary_output + m + "\n" |
| 239 | + return summary_output |
| 240 | + |
12 | 241 | def change_population_dtype_and_round(self, |
13 | 242 | population): |
14 | 243 | """ |
|
0 commit comments