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()