Source code for Config

  1"""
  2Config.py
  3---------
  4This class is instantiated to aquire values from the config file.
  5"""
  6
  7from pathlib import Path
  8from configparser import ConfigParser
  9from configparser import NoOptionError
 10from xml.dom import NotFoundErr
 11from datetime import datetime
 12
 13# TODO Add handling for missing values
 14# NOTE Fails ungracefully at missing values currently
 15
 16FAIL = '\033[91m'
 17ENDC = '\033[0m'
[docs] 18class Config: 19 """ 20 This class is instantiated to aquire values from the config file for the evolutionary run. 21 This object is given to other objects so they can access value sin the config. 22 23 .. todo:: 24 Preexisting todo: Add handling for missing values. This does fail gracefully at missing values currently. 25 """ 26
[docs] 27 def __init__(self, filename): 28 """ 29 Provide the file to be interpreted by the config file. 30 31 Parameters 32 ---------- 33 filename : str 34 The file path of the configuration file. 35 """ 36 self.__config_parser = ConfigParser() 37 self.__config_parser.read(filename) 38 self.__filename = filename
39
[docs] 40 def add_logger(self, logger): 41 """ 42 Adds logger to the Configuration actions 43 44 Parameters 45 ---------- 46 logger : Logger 47 The logger object to log Configuration actions. 48 """ 49 self.__logger = logger
50 51 # SECTION Generic getters for options in the various sections.
[docs] 52 def get_top_parameters(self, param): 53 """ 54 Returns the value of a parameter from the "TOP-LEVEL PARAMETERS" 55 section of the config file. 56 57 Parameters 58 ---------- 59 param : str 60 The name of the parameter to return 61 62 Returns 63 ------- 64 str 65 The value of the parameter 66 """ 67 return self.__config_parser.get("TOP-LEVEL PARAMETERS", param)
68
[docs] 69 def get_fitness_parameters(self, param): 70 """ 71 Returns the value of a parameter from the "FITNESS PARAMETERS" 72 section of the config file. 73 74 Parameters 75 ---------- 76 param : str 77 The name of the parameter to return 78 79 Returns 80 ------- 81 str 82 The value of the parameter 83 """ 84 return self.__config_parser.get("FITNESS PARAMETERS", param)
85
[docs] 86 def get_ga_parameters(self, param): 87 """ 88 Returns the value of a parameter from the "GA PARAMETERS" 89 section of the config file. 90 91 Parameters 92 ---------- 93 param : str 94 The name of the parameter to return 95 96 Returns 97 ------- 98 str 99 The value of the parameter 100 """ 101 return self.__config_parser.get("GA PARAMETERS", param)
102
[docs] 103 def get_init_parameters(self, param): 104 """ 105 Returns the value of a parameter from the "INITIALIZATION PARAMETERS" 106 section of the config file. 107 108 Parameters 109 ---------- 110 param : str 111 The name of the parameter to return 112 113 Returns 114 ------- 115 str 116 The value of the parameter 117 """ 118 return self.__config_parser.get("INITIALIZATION PARAMETERS", param)
119
[docs] 120 def get_stop_parameters(self, param): 121 """ 122 Returns the value of a parameter from the "STOPPING CONDITION PARAMETERS" 123 section of the config file. 124 125 Parameters 126 ---------- 127 param : str 128 The name of the parameter to return 129 130 Returns 131 ------- 132 str 133 The value of the parameter 134 """ 135 return self.__config_parser.get("STOPPING CONDITION PARAMETERS", param)
136
[docs] 137 def get_plotting_parameters(self, param): 138 """ 139 Returns the value of a parameter from the "PLOTTING PARAMETERS" 140 section of the config file. 141 142 Parameters 143 ---------- 144 param : str 145 The name of the parameter to return 146 147 Returns 148 ------- 149 str 150 The value of the parameter 151 """ 152 return self.__config_parser.get("PLOTTING PARAMETERS", param)
153
[docs] 154 def get_logging_parameters(self, param): 155 """ 156 Returns the value of a parameter from the "LOGGING PARAMETERS" 157 section of the config file. 158 159 Parameters 160 ---------- 161 param : str 162 The name of the parameter to return 163 164 Returns 165 ------- 166 str 167 The value of the parameter 168 """ 169 return self.__config_parser.get("LOGGING PARAMETERS", param)
170
[docs] 171 def get_system_parameters(self, param): 172 """ 173 Returns the value of a parameter from the "SYSTEM PARAMETERS" 174 section of the config file. 175 176 Parameters 177 ---------- 178 param : str 179 The name of the parameter to return 180 181 Returns 182 ------- 183 str 184 The value of the parameter 185 """ 186 return self.__config_parser.get("SYSTEM PARAMETERS", param)
187
[docs] 188 def get_hardware_parameters(self, param): 189 """ 190 Returns the value of a parameter from the "HARDWARE PARAMETERS" 191 section of the config file. 192 193 Parameters 194 ---------- 195 param : str 196 The name of the parameter to return 197 198 Returns 199 ------- 200 str 201 The value of the parameter 202 """ 203 return self.__config_parser.get("HARDWARE PARAMETERS", param)
204
[docs] 205 def get_sensitivity_parameters(self, param): 206 """ 207 Returns the value of a parameter from the "FITNESS SENSITIVITY PARAMETERS" 208 section of the config file. 209 210 Parameters 211 ---------- 212 param : str 213 The name of the parameter to return 214 215 Returns 216 ------- 217 str 218 The value of the parameter 219 """ 220 return self.__config_parser.get("FITNESS SENSITIVITY PARAMETERS", param)
221
[docs] 222 def get_transfer_parameters(self, param): 223 """ 224 Returns the value of a parameter from the "TRANSFERABILITY PARAMETERS" 225 section of the config file. 226 227 Parameters 228 ---------- 229 param : str 230 The name of the parameter to return 231 232 Returns 233 ------- 234 str 235 The value of the parameter 236 """ 237 return self.__config_parser.get("TRANSFERABILITY PARAMETERS", param)
238 239 # SECTION Getters for Top-Level Parameters.
[docs] 240 def get_simulation_mode(self): 241 """ 242 Selects the current mode the simulation will run in. These modes are listed below. 243 We verify that only one of the following modes can be returned. 244 245 **FULLY_INTRINSIC** 246 Runs the experiment on the actual hardware. Full normal experiment setup required. 247 **INTRINSIC_SENSITIVITY** 248 Performs Sensitivity analysis. This is done intrensically, but it runs one circuit 249 many times instead of performing evolution on it. 250 **SIM_HARDWARE** 251 Simulation mode. This uses an arbitrary function operating on the compiled binary files 252 that are used to specify the hardware configuration. 253 **FULLY_SIM** 254 Simulation mode. Operates on a small array of arbitrary bit values. 255 256 Returns 257 ------- 258 str 259 The config's selected simulation mode from the list of possible modes. 260 """ 261 input = self.get_top_parameters("SIMULATION_MODE") 262 valid_vals = ["FULLY_INTRINSIC", "FULLY_SIM", "SIM_HARDWARE", "INTRINSIC_SENSITIVITY"] 263 self.check_valid_value("simulation mode", input, valid_vals) 264 return input
265 266 # SECTION Getters for Fitness Parameters.
[docs] 267 def get_fitness_func(self): 268 """ 269 Selects the current Fitness Function Evolution will be using. These modes are listed below 270 We verify that only one of the following modes can be returned. 271 272 **VARIANCE** 273 Variance maximization fitness function. The fitness is the absolute difference 274 of voltage readings from consecutive time steps. Selects for Noise. 275 **PULSE_COUNT** 276 Left in for backwards-compatability. Refers to SENSITIVE_PULSE_COUNT. 277 **TOLERANT_PULSE_COUNT** 278 This uses the number of pulses to generate a fitness. To do this, it compares 279 the calculated frequency from the number of pulses in a second to the target frequency. 280 The closer to the target, the higher the fitness. This fitness function is more 'tolerant' 281 of errors, meaning it assigns greater fitness values to circuits that only have slight errors. 282 **SENSITIVE_PULSE_COUNT** 283 This uses the number of pulses to generate a fitness. To do this, it compares 284 the calculated frequency from the number of pulses in a second to the target frequency. 285 The closer to the target, the higher the fitness. This fitness function is more 'sensitive' 286 of errors, meaning it has an abrupt drop-off in fitness scores even for slight errors. 287 **TONE_DISCRIMINATOR** 288 This randomly alternates between a 1kHz and 10kHz signal sent to the FPGA, and 289 reads in a high/low output from the FGPA to get the predicted frequency. 290 291 Returns 292 ------- 293 str 294 The config's selected fitness function from the list of possible modes. 295 """ 296 input = self.get_fitness_parameters("FITNESS_FUNC") 297 # We're leaving "PULSE_COUNT" for backwards-compatibility 298 # It will use the sensitive function 299 valid_vals = ["VARIANCE", "PULSE_COUNT", "TOLERANT_PULSE_COUNT", "SENSITIVE_PULSE_COUNT", "COMBINED", "PULSE_CONSISTENCY", "TONE_DISCRIMINATOR"] 300 self.check_valid_value("fitness function", input, valid_vals) 301 return input
302
[docs] 303 def get_desired_frequency(self): 304 """ 305 This returns the desired frequency from the config file. It is 306 automatically converted into an integer. 307 If the desired frequency is negitive, this will exit the running program. 308 309 Returns 310 ------- 311 int 312 The desired frequency. Garunteed non-negitive. 313 """ 314 desiredFreq = int(self.get_fitness_parameters("DESIRED_FREQ")) 315 if desiredFreq < 0: 316 self.__log_error(1, "Invalid desired frequency " + str(desiredFreq) + "'. Must be greater than zero.") 317 exit() 318 return desiredFreq
319
[docs] 320 def get_combined_mode(self): 321 """ 322 Selects the current Combined Mode Evolution will be using. 323 These modes are listed below. 324 We verify that only one of the following modes can be returned. 325 326 **ADD** 327 Multiplies weights by fitnesses, then adds the resulting terms to get 328 overall fitness 329 **MULT** 330 Raises fitnesses to the power of their weights, then multiplies the 331 resulting terms to get overall fitness 332 333 Returns 334 ------- 335 str 336 The config's selected combined mode from the list of possible modes. 337 """ 338 input = self.get_fitness_parameters("COMBINED_MODE") 339 valid_vals = ["ADD", "MULT"] 340 self.check_valid_value("combined mode", input, valid_vals) 341 return input
342
[docs] 343 def get_pulse_weight(self): 344 """ 345 This returns the pulse weight from the config file. 346 This is the pulse weight used in the combined_mode operation specified previously. 347 348 Returns 349 ------- 350 float 351 The pulse weight. 352 """ 353 return float(self.get_fitness_parameters("PULSE_WEIGHT"))
354
[docs] 355 def get_var_weight(self): 356 """ 357 This returns the variability weight from the config file. 358 This is the variability weight used in the combined_mode operation specified previously. 359 360 Returns 361 ------- 362 float 363 The variability weight. 364 """ 365 return float(self.get_fitness_parameters("VAR_WEIGHT"))
366
[docs] 367 def get_num_samples(self): 368 """ 369 This returns the number of samples from the config file. 370 371 Returns 372 ------- 373 int 374 The Number of samples from "NUM_SAMPLES" 375 """ 376 value = int(self.get_fitness_parameters("NUM_SAMPLES")) 377 if value < 1: 378 self.__log_error(1, "Invalid number of samples " + str(value) + "'. Must be greater than zero.") 379 exit() 380 return value
381 382 def get_num_passes(self): 383 value = int(self.get_fitness_parameters("NUM_PASSES")) 384 if value < 1: 385 self.__log_error(1, "Invalid number of passes " + str(value) + "'. Must be greater than zero.") 386 exit() 387 return value 388 389 # SECTION Getters for GA Parameters. 390 def get_population_size(self): 391 popSize = int(self.get_ga_parameters("POPULATION_SIZE")) 392 if popSize < 1: 393 self.__log_error(1, "Invalid population size " + str(popSize) + "'. Must be greater than zero.") 394 exit() 395 return popSize 396 397 def get_mutation_probability(self): 398 prob = float(self.get_ga_parameters("MUTATION_PROBABILITY")) 399 if prob < 0.0: 400 self.__log_error(1, "Invalid mutation probability " + str(prob) + "'. Must be greater than zero.") 401 exit() 402 if prob > 1.0: 403 self.__log_error(1, "Invalid mutation probability " + str(prob) + "'. Must be less than one.") 404 exit() 405 return prob 406 407 def get_crossover_probability(self): 408 prob = float(self.get_ga_parameters("CROSSOVER_PROBABILITY")) 409 if prob < 0.0: 410 self.__log_error(1, "Invalid crossover probability " + str(prob) + "'. Must be greater than zero.") 411 exit() 412 if prob > 1.0: 413 self.__log_error(1, "Invalid crossover probability " + str(prob) + "'. Must be less than one.") 414 exit() 415 return prob 416 417 def get_elitism_fraction(self): 418 frac = float(self.get_ga_parameters("ELITISM_FRACTION")) 419 if frac < 0.0: 420 self.__log_error(1, "Invalid elitism fraction " + str(frac) + "'. Must be greater than zero.") 421 exit() 422 if frac > 1.0: 423 self.__log_error(1, "Invalid elistism probability " + str(frac) + "'. Must be less than one.") 424 exit() 425 return frac 426 427 def get_selection_type(self): 428 input = self.get_ga_parameters("SELECTION") 429 valid_vals = ["SINGLE_ELITE", "FRAC_ELITE", "CLASSIC_TOURN", "FIT_PROP_SEL", "RANK_PROP_SEL", "MAP_ELITES"] 430 self.check_valid_value("selection type", input, valid_vals) 431 return input 432 433 def get_random_injection(self): 434 frac = float(self.get_ga_parameters("RANDOM_INJECTION")) 435 if frac < 0.0: 436 self.__log_error(1, "Invalid random injection rate " + str(frac) + "'. Must be greater than zero.") 437 exit() 438 if frac > 1.0: 439 self.__log_error(1, "Invalid random injection rate " + str(frac) + "'. Must be less than one.") 440 exit() 441 return frac 442 443 def get_diversity_measure(self): 444 input = self.get_ga_parameters("DIVERSITY_MEASURE") 445 valid_vals = ["HAMMING_DIST", "UNIQUE", "NONE", "DIFFERING_BITS"] 446 self.check_valid_value("diversity measure", input, valid_vals) 447 return input 448 449 # SECTION Getters for Initialization Parameters. 450 # RANDOM (randomizes all available bits), CLONE_SEED (copies one seed individual to every circuit), 451 # CLONE_SEED_MUTATE (clones the seed but also mutates each individual), EXISTING_POPULATION (uses the existing population files) 452 def get_init_mode(self): 453 input = self.get_init_parameters("INIT_MODE") 454 valid_vals = ["RANDOM", "CLONE_SEED", "CLONE_SEED_MUTATE", "EXISTING_POPULATION"] 455 self.check_valid_value("init mode", input, valid_vals) 456 return input 457 458 def get_randomization_type(self): 459 input = self.get_init_parameters("RANDOMIZE_UNTIL") 460 valid_vals = ["PULSE", "VARIANCE", "VOLTAGE", "NO"] 461 self.check_valid_value("randomization type", input, valid_vals) 462 return input 463 464 def get_randomize_threshold(self): 465 threshold = float(self.get_init_parameters("RANDOMIZE_THRESHOLD")) 466 if threshold < 0: 467 self.__log_error(1, "Invalid random threshold " + str(threshold) + "'. Must be greater than zero.") 468 exit() 469 return threshold 470 471 def get_randomize_mode(self): 472 input = self.get_init_parameters("RANDOMIZE_MODE") 473 valid_vals = ["RANDOM", "MUTATE"] 474 self.check_valid_value("randomization mode", input, valid_vals) 475 return input 476 477 # SECTION Getters for stopping conditions parameters. 478 # Since you can use target fitness instead of gens, we'll need options to see which is turned on 479 # Using both will 480 def using_n_generations(self): 481 return self.get_stop_parameters("GENERATIONS") != "IGNORE" 482 483 def get_n_generations(self): 484 try: 485 nGenerations = int(self.get_stop_parameters("GENERATIONS")) 486 except: 487 self.__log_warning(2, "Non-int user input for number of generations. Program will not terminate based on the number of generations",) 488 return "IGNORE" 489 if nGenerations < 1: 490 self.__log_error(1, "Invalid number of generations " + str(nGenerations) + "'. Must be greater than zero.") 491 exit() 492 return nGenerations 493 494 def using_target_fitness(self): 495 return self.get_stop_parameters("TARGET_FITNESS") != "IGNORE" 496 497 def get_target_fitness(self): 498 try: 499 targetFitness = float(self.get_stop_parameters("TARGET_FITNESS")) 500 except: 501 self.__log_warning(2, "Non-int user input for target fitness. Program will not terminate based on fitness") 502 return "IGNORE" 503 if targetFitness < 0.0: 504 self.__log_error(1, "Invalid target fitness " + str(targetFitness) + "'. Must be greater than zero.") 505 exit() 506 return targetFitness 507 508 # SECTION Getters for fitness sensitivity parameters. 509 def get_test_circuit(self): 510 try: 511 return Path(self.get_sensitivity_parameters("TEST_CIRCUIT")) 512 except NoOptionError: 513 self.__log_error(1, "Invalid file path " + self.get_sensitivity_parameters("TEST_CIRCUIT") + " for test circuit.") 514 515 def using_sensitivity_trials(self): 516 return self.get_sensitivity_parameters("SENSITIVITY_TRIALS") != "IGNORE" 517 518 def get_sensitivity_trials(self): 519 try: 520 trials = int(self.get_sensitivity_parameters("SENSITIVITY_TRIALS")) 521 except: 522 self.__log_warning(1, "Non-int user input for the number of sensitivity trials. Program will not terminate based on the number of of trials") 523 return "IGNORE" 524 if trials < 1: 525 self.__log_error(1, "Invalid number of sensitivity trials" + str(trials) + "'. Must be greater than zero.") 526 exit() 527 return trials 528 529 def using_sensitivity_time(self): 530 return self.get_sensitivity_parameters("SENSITIVITY_TIME") != "IGNORE" 531 532 def get_sensitivity_time(self): 533 try: 534 date_time = datetime.strptime(self.get_sensitivity_parameters("SENSITIVITY_TIME"), '%j:%H:%M:%S') 535 seconds = 86400*date_time.day + 3600*date_time.hour + 60*date_time.minute + date_time.second 536 print(seconds) 537 except ValueError: 538 self.__log_warning(1, "Invalid value for the amount of time to sensitivity trials. Should be in the format %-j:%H:%M:%S. Program will not terminate based on the amount of time passed") 539 return "IGNORE" 540 if seconds < 0: 541 self.__log_error(1, "Invalid amount of time to do sensitivity trials: " + str(seconds) + "'. Must be greater than zero.") 542 exit() 543 return seconds 544 545 def reading_temp_humidity(self): 546 try: 547 input = self.get_sensitivity_parameters("reading_temp_humidity") 548 return input == "true" or input == "True" 549 except NoOptionError: 550 return False 551 552 def get_env_usb_path(self): 553 return self.get_sensitivity_parameters("ENVIRONMENT_USB_PATH") 554 555 #SECTION getts for transferability experiment parameters 556 def using_transfer_interval(self): 557 return isinstance(self.get_transfer_interval(), int) 558 559 def get_transfer_sample(self): 560 return self.get_transfer_parameters("TRANSFER_INTERVAl") == "SAMPLE" 561 562 def get_transfer_interval(self): 563 try: 564 interval = int(self.get_transfer_parameters("TRANSFER_INTERVAl")) 565 except: 566 # self.__log_info(2, "Non-int user input for transfer interval. Evolution will occur on only one FPGA") 567 return "IGNORE" 568 if interval < 1: 569 self.__log_error(1, "Invalid transfer interval size " + str(interval) + "'. Must be greater than zero.") 570 exit() 571 return interval 572 573 def get_fpga2(self): 574 return self.get_transfer_parameters("FPGA2") 575 576 # SECTION Getters for logging parameters. 577 def get_plots_directory(self): 578 try: 579 return Path(self.get_logging_parameters("PLOTS_DIR")) 580 except NoOptionError: 581 return Path("./workspace/plots") 582 583 def get_save_plots(self): 584 try: 585 input = self.get_logging_parameters("save_plots") 586 return input == "true" or input == "True" 587 except NoOptionError: 588 return True 589 590 def saving_population_bistream(self): 591 return isinstance(self.get_population_bistream_save_interval(), int) 592 593 594 def get_population_bistream_save_interval(self): 595 try: 596 interval = int(self.get_logging_parameters("population_bitstream_save_interval")) 597 except: 598 return "IGNORE" 599 if interval < 1: 600 self.__log_error(1, "Invalid population bistream save interval " + str(interval) + "'. Must be greater than zero.") 601 exit() 602 return interval 603 604 605 def get_output_directory(self): 606 try: 607 return Path(self.get_logging_parameters("OUTPUT_DIR")) 608 except NoOptionError: 609 return Path("./prev_workspaces") 610 611 def get_final_experiment_directory(self): 612 try: 613 return Path(self.get_logging_parameters("final_experiment_dir")) 614 except NoOptionError: 615 return Path("./experiments") 616 617 def get_backup_workspace(self): 618 try: 619 input = self.get_logging_parameters("backup_workspace") 620 return input == "true" or input == "True" 621 except NoOptionError: 622 return True 623 624 def get_asc_directory(self): 625 try: 626 return Path(self.get_logging_parameters("ASC_DIR")) 627 except NoOptionError: 628 return Path("./workspace/experiment_asc") 629 630 def get_bin_directory(self): 631 try: 632 return Path(self.get_logging_parameters("BIN_DIR")) 633 except NoOptionError: 634 return Path("./workspace/experiment_bin") 635 636 def get_data_directory(self): 637 try: 638 return Path(self.get_logging_parameters("DATA_DIR")) 639 except NoOptionError: 640 return Path("./workspace/experiment_data") 641 642 def get_analysis_directory(self): 643 try: 644 return Path(self.get_logging_parameters("ANALYSIS")) 645 except NoOptionError: 646 return Path("./workspace/analysis") 647 648 def get_generations_directory(self): 649 try: 650 return Path(self.get_logging_parameters("GENERATIONS_DIR")) 651 except NoOptionError: 652 return Path("./workspace/generations") 653 654 def get_log_file(self): 655 try: 656 return Path(self.get_logging_parameters("LOG_FILE")) 657 except NoOptionError: 658 return Path("./workspace/log") 659 660 def get_save_log(self): 661 try: 662 input = self.get_logging_parameters("save_log") 663 return input == "true" or input == "True" 664 except NoOptionError: 665 return True 666 667 def get_datetime_format(self): 668 try: 669 return self.get_logging_parameters("DATETIME_FORMAT") 670 except NoOptionError: 671 return Path("%%m/%%d/%%Y - %%H:%%M:%%S") 672 673 def get_best_file(self): 674 try: 675 return self.get_logging_parameters("BEST_FILE") 676 except NoOptionError: 677 return Path("./workspace/best.asc") 678 679 def get_src_pops_dir(self): 680 try: 681 return Path(self.get_logging_parameters("SRC_POPULATIONS_DIR")) 682 except NoOptionError: 683 return Path("./workspace/source_population") 684 685 # There are 5 log levels (0-4) 686 # 4 will log the most information, 1 will log the least 687 # 0 will log nothing 688 # So when putting a log level as an arg to a log event, higher numbers = seen less often 689 def get_log_level(self): 690 input = int(self.get_logging_parameters("LOG_LEVEL")) 691 valid_vals = [0, 1, 2, 3, 4, 5] 692 self.check_valid_value("logging level", input, valid_vals) 693 return input 694 695 def get_use_ovr_best(self): 696 try: 697 input = self.get_logging_parameters("show_ovr_best") 698 return input == "true" or input == "True" 699 except NoOptionError: 700 return True 701 702 # SECTION Getters for system parameters. 703 def get_fpga(self): 704 return self.get_system_parameters("FPGA") 705 706 def get_usb_path(self): 707 return self.get_system_parameters("USB_PATH") 708 709 def get_upload_to_arduino(self): 710 value = self.get_system_parameters("auto_upload_to_arduino") 711 return value== "true" or value == "True" 712 713 # SECTION Getters for hardware parameters 714 def get_routing_type(self): 715 input = self.get_hardware_parameters("ROUTING") 716 valid_vals = ["MOORE", "NEWSE"] 717 self.check_valid_value("routing type", input, valid_vals) 718 return input 719 720 def get_serial_baud(self): 721 return int(self.get_hardware_parameters("SERIAL_BAUD")) 722 723 def get_accessed_columns(self): 724 return self.get_hardware_parameters("ACCESSED_COLUMNS").split(",") 725 726 def get_using_configurable_io(self): 727 input = self.get_hardware_parameters("configurable_io") 728 return input == "true" or input == "True" 729 730 def get_input_pins(self): 731 valid_vals = [112, 113, 114, 115, 116, 117, 118, 119, 44, 45, 47, 48, 56, 60, 61, 62] 732 pins = self.get_hardware_parameters("INPUT_PINS").split(",") 733 for pin in pins: 734 self.check_valid_value("input pin", int(pin), valid_vals) 735 return pins 736 737 def get_output_pins(self): 738 valid_vals = [112, 113, 114, 115, 116, 117, 118, 119, 44, 45, 47, 48, 56, 60, 61, 62] 739 pins = self.get_hardware_parameters("OUTPUT_PINS").split(",") 740 for pin in pins: 741 self.check_valid_value("output pin", int(pin), valid_vals) 742 return pins 743 744 def get_mcu_read_timeout(self): 745 return float(self.get_hardware_parameters("MCU_READ_TIMEOUT")) 746 747 def get_launch_plots(self): 748 value = self.get_plotting_parameters("launch_plots") 749 return value == "true" or value == "True" 750 751 def get_frame_interval(self): 752 return int(self.get_plotting_parameters("frame_interval")) 753 754 def check_valid_value(self, param_name, user_input, allowed_values): 755 if not user_input in allowed_values: 756 self.__log_error(1, "Invalid " + param_name + " '" + str(user_input) + "'. Valid parameters are: " + 757 ", ".join(list(map(lambda x: str(x), allowed_values)))) 758 exit() 759 760 def validate_all(self): 761 self.get_simulation_mode() 762 self.validate_fitness_params() 763 764 if self.get_simulation_mode == 'INTRINSIC_SENSITIVITY': 765 self.validate_sensitivity_params() 766 else: 767 self.validate_ga_params() 768 self.validate_init_params() 769 self.validate_stopping_params() 770 771 self.validate_logging_params() 772 773 if self.get_simulation_mode != 'FULLY_SIM' and self.get_simulation_mode != 'SIM_HARDWARE': 774 self.validate_system_params() 775 self.validate_hardware_params() 776 777 self.validate_plotting_params() 778 779 # Make sure user follows our requirements 780 # Pulse consistency must have >=1 passes and >=1 samples 781 if self.get_fitness_func() == "PULSE_CONSISTENCY" and (self.get_num_passes() * self.get_num_samples()) <= 1: 782 self.__log_error(1, "PULSE_CONSISTENCY function can only be used with multiple samples/passes") 783 exit() 784 # MAP elites can only be used with VARIANCE, COMBINED, and PULSE CONSISTENCY 785 if self.get_selection_type() == "MAP_ELITES": 786 if self.get_fitness_func() not in ["VARIANCE", "COMBINED", "PULSE_CONSISTENCY"]: 787 self.__log_error(1, "MAP_ELITES selection can only be used with the following fitness functions: " + 788 "VARIANCE, COMBINED, PULSE_CONSISTENCY") 789 exit() 790 791 # True if the fitness function counts pulses 792 def is_pulse_func(self): 793 return (self.get_fitness_func() == 'PULSE_COUNT' or self.get_fitness_func() == 'TOLERANT_PULSE_COUNT' 794 or self.get_fitness_func() == 'SENSITIVE_PULSE_COUNT' or self.get_fitness_func() == 'PULSE_CONSISTENCY') 795 796 # Contrary to the above, this only returns true if the target is to count pulses for a target frequency 797 def is_pulse_count(self): 798 return (self.get_fitness_func() == 'PULSE_COUNT' or self.get_fitness_func() == 'TOLERANT_PULSE_COUNT' 799 or self.get_fitness_func() == 'SENSITIVE_PULSE_COUNT') 800 801 def get_map_elites_dimension(self): 802 if self.get_fitness_func() in ['PULSE_CONSISTENCY']: 803 return 1 804 elif self.get_fitness_func() in ['VARIANCE', 'COMBINED']: 805 return 2 806 807 def validate_fitness_params(self): 808 self.get_fitness_func() 809 810 if self.get_fitness_func() == "COMBINED": 811 self.get_combined_mode() 812 self.get_pulse_weight() 813 self.get_var_weight() 814 815 if self.is_pulse_func(): 816 self.get_desired_frequency() 817 self.get_num_samples() 818 self.get_num_passes() 819 820 def validate_ga_params(self): 821 self.get_population_size() 822 self.get_mutation_probability() 823 self.get_crossover_probability() 824 self.get_elitism_fraction() 825 self.get_selection_type() 826 self.get_diversity_measure() 827 self.get_random_injection() 828 829 def validate_init_params(self): 830 self.get_init_mode() 831 self.get_randomization_type() 832 self.get_randomize_threshold() 833 self.get_randomize_mode() 834 835 def validate_stopping_params(self): 836 self.using_n_generations() 837 self.get_n_generations() 838 self.using_target_fitness() 839 self.get_target_fitness() 840 841 def validate_logging_params(self): 842 self.get_log_level() 843 self.get_save_log() 844 self.get_save_plots() 845 self.get_log_file() 846 self.get_plots_directory() 847 self.get_asc_directory() 848 self.get_bin_directory() 849 self.get_data_directory() 850 self.get_analysis_directory() 851 self.get_best_file() 852 self.get_src_pops_dir() 853 self.get_datetime_format() 854 self.get_generations_directory() 855 self.get_use_ovr_best() 856 857 def validate_system_params(self): 858 self.get_fpga() 859 self.get_usb_path() 860 861 def validate_hardware_params(self): 862 self.get_routing_type() 863 self.get_serial_baud() 864 self.get_accessed_columns() 865 self.get_mcu_read_timeout() 866 if self.get_using_configurable_io(): 867 self.get_input_pins() 868 self.get_output_pins() 869 870 def validate_plotting_params(self): 871 self.get_launch_plots() 872 self.get_frame_interval() 873 874 def validate_sensitivity_params(self): 875 self.get_test_circuit() 876 self.get_sensitivity_trials() 877 878 def __log_event(self, level, *event): 879 """ 880 Emit an event-level log. This function is fulfilled through 881 the logger. 882 """ 883 self.__logger.log_event(level, *event) 884 885 def __log_info(self, level, *info): 886 """ 887 Emit an info-level log. This function is fulfilled through 888 the logger. 889 """ 890 self.__logger.log_info(level, *info) 891 892 def __log_error(self, level, *error): 893 """ 894 Emit an error-level log. This function is fulfilled through 895 the logger. 896 """ 897 self.__logger.log_error(level, *error) 898 899 def __log_warning(self, level, *warning): 900 """ 901 Emit a warning-level log. This function is fulfilled through 902 the logger. 903 """ 904 self.__logger.log_warning(level, *warning) 905 906 # used by the logger to store a back-up of the config 907 # probably not the cleanest way to do this 908 def get_raw_data(self): 909 f = open(self.__filename, "r") 910 return f.read()