1*760c253cSXin Li# Copyright 2013 The ChromiumOS Authors 2*760c253cSXin Li# Use of this source code is governed by a BSD-style license that can be 3*760c253cSXin Li# found in the LICENSE file. 4*760c253cSXin Li"""Hill climbing unitest. 5*760c253cSXin Li 6*760c253cSXin LiPart of the Chrome build flags optimization. 7*760c253cSXin Li 8*760c253cSXin LiTest the best branching hill climbing algorithms, genetic algorithm and 9*760c253cSXin Liiterative elimination algorithm. 10*760c253cSXin Li""" 11*760c253cSXin Li 12*760c253cSXin Li__author__ = "[email protected] (Yuheng Long)" 13*760c253cSXin Li 14*760c253cSXin Liimport multiprocessing 15*760c253cSXin Liimport random 16*760c253cSXin Liimport sys 17*760c253cSXin Liimport unittest 18*760c253cSXin Li 19*760c253cSXin Liimport flags 20*760c253cSXin Lifrom flags import Flag 21*760c253cSXin Lifrom flags import FlagSet 22*760c253cSXin Lifrom genetic_algorithm import GAGeneration 23*760c253cSXin Lifrom genetic_algorithm import GATask 24*760c253cSXin Lifrom hill_climb_best_neighbor import HillClimbingBestBranch 25*760c253cSXin Lifrom iterative_elimination import IterativeEliminationFirstGeneration 26*760c253cSXin Liimport pipeline_process 27*760c253cSXin Lifrom steering import Steering 28*760c253cSXin Lifrom task import BUILD_STAGE 29*760c253cSXin Lifrom task import Task 30*760c253cSXin Lifrom task import TEST_STAGE 31*760c253cSXin Li 32*760c253cSXin Li 33*760c253cSXin Li# The number of flags be tested. 34*760c253cSXin LiNUM_FLAGS = 5 35*760c253cSXin Li 36*760c253cSXin Li# The value range of the flags. 37*760c253cSXin LiFLAG_RANGES = 10 38*760c253cSXin Li 39*760c253cSXin Li# The following variables are meta data for the Genetic Algorithm. 40*760c253cSXin LiSTOP_THRESHOLD = 20 41*760c253cSXin LiNUM_CHROMOSOMES = 10 42*760c253cSXin LiNUM_TRIALS = 20 43*760c253cSXin LiMUTATION_RATE = 0.03 44*760c253cSXin Li 45*760c253cSXin Li 46*760c253cSXin Lidef _GenerateRandomRasks(specs): 47*760c253cSXin Li """Generate a task that has random values. 48*760c253cSXin Li 49*760c253cSXin Li Args: 50*760c253cSXin Li specs: A list of spec from which the flag set is created. 51*760c253cSXin Li 52*760c253cSXin Li Returns: 53*760c253cSXin Li A set containing a task that has random values. 54*760c253cSXin Li """ 55*760c253cSXin Li 56*760c253cSXin Li flag_set = [] 57*760c253cSXin Li 58*760c253cSXin Li for spec in specs: 59*760c253cSXin Li numeric_flag_match = flags.Search(spec) 60*760c253cSXin Li if numeric_flag_match: 61*760c253cSXin Li # Numeric flags. 62*760c253cSXin Li start = int(numeric_flag_match.group("start")) 63*760c253cSXin Li end = int(numeric_flag_match.group("end")) 64*760c253cSXin Li 65*760c253cSXin Li value = random.randint(start - 1, end - 1) 66*760c253cSXin Li if value != start - 1: 67*760c253cSXin Li # If the value falls in the range, this flag is enabled. 68*760c253cSXin Li flag_set.append(Flag(spec, value)) 69*760c253cSXin Li else: 70*760c253cSXin Li # Boolean flags. 71*760c253cSXin Li if random.randint(0, 1): 72*760c253cSXin Li flag_set.append(Flag(spec)) 73*760c253cSXin Li 74*760c253cSXin Li return set([Task(FlagSet(flag_set))]) 75*760c253cSXin Li 76*760c253cSXin Li 77*760c253cSXin Lidef _GenerateAllFlagsTasks(specs): 78*760c253cSXin Li """Generate a task that all the flags are enable. 79*760c253cSXin Li 80*760c253cSXin Li All the boolean flags in the specs will be enabled and all the numeric flag 81*760c253cSXin Li with have the largest legal value. 82*760c253cSXin Li 83*760c253cSXin Li Args: 84*760c253cSXin Li specs: A list of spec from which the flag set is created. 85*760c253cSXin Li 86*760c253cSXin Li Returns: 87*760c253cSXin Li A set containing a task that has all flags enabled. 88*760c253cSXin Li """ 89*760c253cSXin Li 90*760c253cSXin Li flag_set = [] 91*760c253cSXin Li 92*760c253cSXin Li for spec in specs: 93*760c253cSXin Li numeric_flag_match = flags.Search(spec) 94*760c253cSXin Li 95*760c253cSXin Li if numeric_flag_match: 96*760c253cSXin Li value = int(numeric_flag_match.group("end")) - 1 97*760c253cSXin Li else: 98*760c253cSXin Li value = -1 99*760c253cSXin Li flag_set.append(Flag(spec, value)) 100*760c253cSXin Li 101*760c253cSXin Li return set([Task(FlagSet(flag_set))]) 102*760c253cSXin Li 103*760c253cSXin Li 104*760c253cSXin Lidef _GenerateNoFlagTask(): 105*760c253cSXin Li return set([Task(FlagSet([]))]) 106*760c253cSXin Li 107*760c253cSXin Li 108*760c253cSXin Lidef GenerateRandomGATasks(specs, num_tasks, num_trials): 109*760c253cSXin Li """Generate a set of tasks for the Genetic Algorithm. 110*760c253cSXin Li 111*760c253cSXin Li Args: 112*760c253cSXin Li specs: A list of spec from which the flag set is created. 113*760c253cSXin Li num_tasks: number of tasks that should be generated. 114*760c253cSXin Li num_trials: the maximum number of tries should be attempted to generate the 115*760c253cSXin Li set of tasks. 116*760c253cSXin Li 117*760c253cSXin Li Returns: 118*760c253cSXin Li A set of randomly generated tasks. 119*760c253cSXin Li """ 120*760c253cSXin Li 121*760c253cSXin Li tasks = set([]) 122*760c253cSXin Li 123*760c253cSXin Li total_trials = 0 124*760c253cSXin Li while len(tasks) < num_tasks and total_trials < num_trials: 125*760c253cSXin Li new_flag = FlagSet( 126*760c253cSXin Li [Flag(spec) for spec in specs if random.randint(0, 1)] 127*760c253cSXin Li ) 128*760c253cSXin Li new_task = GATask(new_flag) 129*760c253cSXin Li 130*760c253cSXin Li if new_task in tasks: 131*760c253cSXin Li total_trials += 1 132*760c253cSXin Li else: 133*760c253cSXin Li tasks.add(new_task) 134*760c253cSXin Li total_trials = 0 135*760c253cSXin Li 136*760c253cSXin Li return tasks 137*760c253cSXin Li 138*760c253cSXin Li 139*760c253cSXin Lidef _GenerateInitialFlags(specs, spec): 140*760c253cSXin Li """Generate the flag_set of a task in the flag elimination algorithm. 141*760c253cSXin Li 142*760c253cSXin Li Set the value of all the flags to the largest value, except for the flag that 143*760c253cSXin Li contains spec. 144*760c253cSXin Li 145*760c253cSXin Li For example, if the specs are [-finline-limit=[1-1000], -fstrict-aliasing] and 146*760c253cSXin Li the spec is -finline-limit=[1-1000], then the result is 147*760c253cSXin Li [-finline-limit=[1-1000]:-finline-limit=998, 148*760c253cSXin Li -fstrict-aliasing:-fstrict-aliasing] 149*760c253cSXin Li 150*760c253cSXin Li Args: 151*760c253cSXin Li specs: an array of specifications from which the result flag_set is created. 152*760c253cSXin Li The flag_set contains one and only one flag that contain the specification 153*760c253cSXin Li spec. 154*760c253cSXin Li spec: The flag containing this spec should have a value that is smaller than 155*760c253cSXin Li the highest value the flag can have. 156*760c253cSXin Li 157*760c253cSXin Li Returns: 158*760c253cSXin Li An array of flags, each of which contains one spec in specs. All the values 159*760c253cSXin Li of the flags are the largest values in specs, expect the one that contains 160*760c253cSXin Li spec. 161*760c253cSXin Li """ 162*760c253cSXin Li 163*760c253cSXin Li flag_set = [] 164*760c253cSXin Li for other_spec in specs: 165*760c253cSXin Li numeric_flag_match = flags.Search(other_spec) 166*760c253cSXin Li # Found the spec in the array specs. 167*760c253cSXin Li if other_spec == spec: 168*760c253cSXin Li # Numeric flag will have a value that is smaller than the largest value 169*760c253cSXin Li # and Boolean flag will be deleted. 170*760c253cSXin Li if numeric_flag_match: 171*760c253cSXin Li end = int(numeric_flag_match.group("end")) 172*760c253cSXin Li flag_set.append(flags.Flag(other_spec, end - 2)) 173*760c253cSXin Li 174*760c253cSXin Li continue 175*760c253cSXin Li 176*760c253cSXin Li # other_spec != spec 177*760c253cSXin Li if numeric_flag_match: 178*760c253cSXin Li # numeric flag 179*760c253cSXin Li end = int(numeric_flag_match.group("end")) 180*760c253cSXin Li flag_set.append(flags.Flag(other_spec, end - 1)) 181*760c253cSXin Li continue 182*760c253cSXin Li 183*760c253cSXin Li # boolean flag 184*760c253cSXin Li flag_set.append(flags.Flag(other_spec)) 185*760c253cSXin Li 186*760c253cSXin Li return flag_set 187*760c253cSXin Li 188*760c253cSXin Li 189*760c253cSXin Lidef _GenerateAllIterativeEliminationTasks(specs): 190*760c253cSXin Li """Generate the initial tasks for the negative flag elimination algorithm. 191*760c253cSXin Li 192*760c253cSXin Li Generate the base line task that turns on all the boolean flags and sets the 193*760c253cSXin Li value to be the largest value for the numeric flag. 194*760c253cSXin Li 195*760c253cSXin Li For example, if the specs are [-finline-limit=[1-1000], -fstrict-aliasing], 196*760c253cSXin Li the base line is [-finline-limit=[1-1000]:-finline-limit=999, 197*760c253cSXin Li -fstrict-aliasing:-fstrict-aliasing] 198*760c253cSXin Li 199*760c253cSXin Li Generate a set of task, each turns off one of the flag or sets a value that is 200*760c253cSXin Li smaller than the largest value for the flag. 201*760c253cSXin Li 202*760c253cSXin Li Args: 203*760c253cSXin Li specs: an array of specifications from which the result flag_set is created. 204*760c253cSXin Li 205*760c253cSXin Li Returns: 206*760c253cSXin Li An array containing one generation of the initial tasks for the negative 207*760c253cSXin Li flag elimination algorithm. 208*760c253cSXin Li """ 209*760c253cSXin Li 210*760c253cSXin Li # The set of tasks to be generated. 211*760c253cSXin Li results = set([]) 212*760c253cSXin Li flag_set = [] 213*760c253cSXin Li 214*760c253cSXin Li for spec in specs: 215*760c253cSXin Li numeric_flag_match = flags.Search(spec) 216*760c253cSXin Li if numeric_flag_match: 217*760c253cSXin Li # Numeric flag. 218*760c253cSXin Li end_value = int(numeric_flag_match.group("end")) 219*760c253cSXin Li flag_set.append(flags.Flag(spec, end_value - 1)) 220*760c253cSXin Li continue 221*760c253cSXin Li 222*760c253cSXin Li # Boolean flag. 223*760c253cSXin Li flag_set.append(flags.Flag(spec)) 224*760c253cSXin Li 225*760c253cSXin Li # The base line task that set all the flags to their largest values. 226*760c253cSXin Li parent_task = Task(flags.FlagSet(flag_set)) 227*760c253cSXin Li results.add(parent_task) 228*760c253cSXin Li 229*760c253cSXin Li for spec in specs: 230*760c253cSXin Li results.add(Task(flags.FlagSet(_GenerateInitialFlags(specs, spec)))) 231*760c253cSXin Li 232*760c253cSXin Li return [IterativeEliminationFirstGeneration(results, parent_task)] 233*760c253cSXin Li 234*760c253cSXin Li 235*760c253cSXin Lidef _ComputeCost(cost_func, specs, flag_set): 236*760c253cSXin Li """Compute the mock cost of the flag_set using the input cost function. 237*760c253cSXin Li 238*760c253cSXin Li All the boolean flags in the specs will be enabled and all the numeric flag 239*760c253cSXin Li with have the largest legal value. 240*760c253cSXin Li 241*760c253cSXin Li Args: 242*760c253cSXin Li cost_func: The cost function which is used to compute the mock cost of a 243*760c253cSXin Li dictionary of flags. 244*760c253cSXin Li specs: All the specs that are used in the algorithm. This is used to check 245*760c253cSXin Li whether certain flag is disabled in the flag_set dictionary. 246*760c253cSXin Li flag_set: a dictionary of the spec and flag pairs. 247*760c253cSXin Li 248*760c253cSXin Li Returns: 249*760c253cSXin Li The mock cost of the input dictionary of the flags. 250*760c253cSXin Li """ 251*760c253cSXin Li 252*760c253cSXin Li values = [] 253*760c253cSXin Li 254*760c253cSXin Li for spec in specs: 255*760c253cSXin Li # If a flag is enabled, its value is added. Otherwise a padding 0 is added. 256*760c253cSXin Li values.append(flag_set[spec].GetValue() if spec in flag_set else 0) 257*760c253cSXin Li 258*760c253cSXin Li # The cost function string can use the values array. 259*760c253cSXin Li return eval(cost_func) 260*760c253cSXin Li 261*760c253cSXin Li 262*760c253cSXin Lidef _GenerateTestFlags(num_flags, upper_bound, file_name): 263*760c253cSXin Li """Generate a set of mock flags and write it to a configuration file. 264*760c253cSXin Li 265*760c253cSXin Li Generate a set of mock flags 266*760c253cSXin Li 267*760c253cSXin Li Args: 268*760c253cSXin Li num_flags: Number of numeric flags to be generated. 269*760c253cSXin Li upper_bound: The value of the upper bound of the range. 270*760c253cSXin Li file_name: The configuration file name into which the mock flags are put. 271*760c253cSXin Li """ 272*760c253cSXin Li 273*760c253cSXin Li with open(file_name, "w") as output_file: 274*760c253cSXin Li num_flags = int(num_flags) 275*760c253cSXin Li upper_bound = int(upper_bound) 276*760c253cSXin Li for i in range(num_flags): 277*760c253cSXin Li output_file.write("%s=[1-%d]\n" % (i, upper_bound)) 278*760c253cSXin Li 279*760c253cSXin Li 280*760c253cSXin Lidef _TestAlgorithm(cost_func, specs, generations, best_result): 281*760c253cSXin Li """Test the best result the algorithm should return. 282*760c253cSXin Li 283*760c253cSXin Li Set up the framework, run the input algorithm and verify the result. 284*760c253cSXin Li 285*760c253cSXin Li Args: 286*760c253cSXin Li cost_func: The cost function which is used to compute the mock cost of a 287*760c253cSXin Li dictionary of flags. 288*760c253cSXin Li specs: All the specs that are used in the algorithm. This is used to check 289*760c253cSXin Li whether certain flag is disabled in the flag_set dictionary. 290*760c253cSXin Li generations: The initial generations to be evaluated. 291*760c253cSXin Li best_result: The expected best result of the algorithm. If best_result is 292*760c253cSXin Li -1, the algorithm may or may not return the best value. Therefore, no 293*760c253cSXin Li assertion will be inserted. 294*760c253cSXin Li """ 295*760c253cSXin Li 296*760c253cSXin Li # Set up the utilities to test the framework. 297*760c253cSXin Li manager = multiprocessing.Manager() 298*760c253cSXin Li input_queue = manager.Queue() 299*760c253cSXin Li output_queue = manager.Queue() 300*760c253cSXin Li pp_steer = multiprocessing.Process( 301*760c253cSXin Li target=Steering, args=(set(), generations, output_queue, input_queue) 302*760c253cSXin Li ) 303*760c253cSXin Li pp_steer.start() 304*760c253cSXin Li 305*760c253cSXin Li # The best result of the algorithm so far. 306*760c253cSXin Li result = sys.maxint 307*760c253cSXin Li 308*760c253cSXin Li while True: 309*760c253cSXin Li task = input_queue.get() 310*760c253cSXin Li 311*760c253cSXin Li # POISONPILL signal the ends of the algorithm. 312*760c253cSXin Li if task == pipeline_process.POISONPILL: 313*760c253cSXin Li break 314*760c253cSXin Li 315*760c253cSXin Li task.SetResult(BUILD_STAGE, (0, 0, 0, 0, 0)) 316*760c253cSXin Li 317*760c253cSXin Li # Compute the mock cost for the task. 318*760c253cSXin Li task_result = _ComputeCost(cost_func, specs, task.GetFlags()) 319*760c253cSXin Li task.SetResult(TEST_STAGE, task_result) 320*760c253cSXin Li 321*760c253cSXin Li # If the mock result of the current task is the best so far, set this 322*760c253cSXin Li # result to be the best result. 323*760c253cSXin Li if task_result < result: 324*760c253cSXin Li result = task_result 325*760c253cSXin Li 326*760c253cSXin Li output_queue.put(task) 327*760c253cSXin Li 328*760c253cSXin Li pp_steer.join() 329*760c253cSXin Li 330*760c253cSXin Li # Only do this test when best_result is not -1. 331*760c253cSXin Li if best_result != -1: 332*760c253cSXin Li assert best_result == result 333*760c253cSXin Li 334*760c253cSXin Li 335*760c253cSXin Liclass MockAlgorithmsTest(unittest.TestCase): 336*760c253cSXin Li """This class mock tests different steering algorithms. 337*760c253cSXin Li 338*760c253cSXin Li The steering algorithms are responsible for generating the next set of tasks 339*760c253cSXin Li to run in each iteration. This class does a functional testing on the 340*760c253cSXin Li algorithms. It mocks out the computation of the fitness function from the 341*760c253cSXin Li build and test phases by letting the user define the fitness function. 342*760c253cSXin Li """ 343*760c253cSXin Li 344*760c253cSXin Li def _GenerateFlagSpecifications(self): 345*760c253cSXin Li """Generate the testing specifications.""" 346*760c253cSXin Li 347*760c253cSXin Li mock_test_file = "scale_mock_test" 348*760c253cSXin Li _GenerateTestFlags(NUM_FLAGS, FLAG_RANGES, mock_test_file) 349*760c253cSXin Li return flags.ReadConf(mock_test_file) 350*760c253cSXin Li 351*760c253cSXin Li def testBestHillClimb(self): 352*760c253cSXin Li """Test the best hill climb algorithm. 353*760c253cSXin Li 354*760c253cSXin Li Test whether it finds the best results as expected. 355*760c253cSXin Li """ 356*760c253cSXin Li 357*760c253cSXin Li # Initiate the build/test command and the log directory. 358*760c253cSXin Li Task.InitLogCommand(None, None, "output") 359*760c253cSXin Li 360*760c253cSXin Li # Generate the testing specs. 361*760c253cSXin Li specs = self._GenerateFlagSpecifications() 362*760c253cSXin Li 363*760c253cSXin Li # Generate the initial generations for a test whose cost function is the 364*760c253cSXin Li # summation of the values of all the flags. 365*760c253cSXin Li generation_tasks = _GenerateAllFlagsTasks(specs) 366*760c253cSXin Li generations = [HillClimbingBestBranch(generation_tasks, set([]), specs)] 367*760c253cSXin Li 368*760c253cSXin Li # Test the algorithm. The cost function is the summation of all the values 369*760c253cSXin Li # of all the flags. Therefore, the best value is supposed to be 0, i.e., 370*760c253cSXin Li # when all the flags are disabled. 371*760c253cSXin Li _TestAlgorithm("sum(values[0:len(values)])", specs, generations, 0) 372*760c253cSXin Li 373*760c253cSXin Li # This test uses a cost function that is the negative of the previous cost 374*760c253cSXin Li # function. Therefore, the best result should be found in task with all the 375*760c253cSXin Li # flags enabled. 376*760c253cSXin Li cost_function = "sys.maxint - sum(values[0:len(values)])" 377*760c253cSXin Li all_flags = list(generation_tasks)[0].GetFlags() 378*760c253cSXin Li cost = _ComputeCost(cost_function, specs, all_flags) 379*760c253cSXin Li 380*760c253cSXin Li # Generate the initial generations. 381*760c253cSXin Li generation_tasks = _GenerateNoFlagTask() 382*760c253cSXin Li generations = [HillClimbingBestBranch(generation_tasks, set([]), specs)] 383*760c253cSXin Li 384*760c253cSXin Li # Test the algorithm. The cost function is negative of the summation of all 385*760c253cSXin Li # the values of all the flags. Therefore, the best value is supposed to be 386*760c253cSXin Li # 0, i.e., when all the flags are disabled. 387*760c253cSXin Li _TestAlgorithm(cost_function, specs, generations, cost) 388*760c253cSXin Li 389*760c253cSXin Li def testGeneticAlgorithm(self): 390*760c253cSXin Li """Test the Genetic Algorithm. 391*760c253cSXin Li 392*760c253cSXin Li Do a functional testing here and see how well it scales. 393*760c253cSXin Li """ 394*760c253cSXin Li 395*760c253cSXin Li # Initiate the build/test command and the log directory. 396*760c253cSXin Li Task.InitLogCommand(None, None, "output") 397*760c253cSXin Li 398*760c253cSXin Li # Generate the testing specs. 399*760c253cSXin Li specs = self._GenerateFlagSpecifications() 400*760c253cSXin Li # Initiate the build/test command and the log directory. 401*760c253cSXin Li GAGeneration.InitMetaData( 402*760c253cSXin Li STOP_THRESHOLD, NUM_CHROMOSOMES, NUM_TRIALS, specs, MUTATION_RATE 403*760c253cSXin Li ) 404*760c253cSXin Li 405*760c253cSXin Li # Generate the initial generations. 406*760c253cSXin Li generation_tasks = GenerateRandomGATasks( 407*760c253cSXin Li specs, NUM_CHROMOSOMES, NUM_TRIALS 408*760c253cSXin Li ) 409*760c253cSXin Li generations = [GAGeneration(generation_tasks, set([]), 0)] 410*760c253cSXin Li 411*760c253cSXin Li # Test the algorithm. 412*760c253cSXin Li _TestAlgorithm("sum(values[0:len(values)])", specs, generations, -1) 413*760c253cSXin Li cost_func = "sys.maxint - sum(values[0:len(values)])" 414*760c253cSXin Li _TestAlgorithm(cost_func, specs, generations, -1) 415*760c253cSXin Li 416*760c253cSXin Li def testIterativeElimination(self): 417*760c253cSXin Li """Test the iterative elimination algorithm. 418*760c253cSXin Li 419*760c253cSXin Li Test whether it finds the best results as expected. 420*760c253cSXin Li """ 421*760c253cSXin Li 422*760c253cSXin Li # Initiate the build/test command and the log directory. 423*760c253cSXin Li Task.InitLogCommand(None, None, "output") 424*760c253cSXin Li 425*760c253cSXin Li # Generate the testing specs. 426*760c253cSXin Li specs = self._GenerateFlagSpecifications() 427*760c253cSXin Li 428*760c253cSXin Li # Generate the initial generations. The generation contains the base line 429*760c253cSXin Li # task that turns on all the flags and tasks that each turn off one of the 430*760c253cSXin Li # flags. 431*760c253cSXin Li generations = _GenerateAllIterativeEliminationTasks(specs) 432*760c253cSXin Li 433*760c253cSXin Li # Test the algorithm. The cost function is the summation of all the values 434*760c253cSXin Li # of all the flags. Therefore, the best value is supposed to be 0, i.e., 435*760c253cSXin Li # when all the flags are disabled. 436*760c253cSXin Li _TestAlgorithm("sum(values[0:len(values)])", specs, generations, 0) 437*760c253cSXin Li 438*760c253cSXin Li # This test uses a cost function that is the negative of the previous cost 439*760c253cSXin Li # function. Therefore, the best result should be found in task with all the 440*760c253cSXin Li # flags enabled. 441*760c253cSXin Li all_flags_tasks = _GenerateAllFlagsTasks(specs) 442*760c253cSXin Li cost_function = "sys.maxint - sum(values[0:len(values)])" 443*760c253cSXin Li # Compute the cost of the task that turns on all the flags. 444*760c253cSXin Li all_flags = list(all_flags_tasks)[0].GetFlags() 445*760c253cSXin Li cost = _ComputeCost(cost_function, specs, all_flags) 446*760c253cSXin Li 447*760c253cSXin Li # Test the algorithm. The cost function is negative of the summation of all 448*760c253cSXin Li # the values of all the flags. Therefore, the best value is supposed to be 449*760c253cSXin Li # 0, i.e., when all the flags are disabled. 450*760c253cSXin Li # The concrete type of the generation decides how the next generation will 451*760c253cSXin Li # be generated. 452*760c253cSXin Li _TestAlgorithm(cost_function, specs, generations, cost) 453*760c253cSXin Li 454*760c253cSXin Li 455*760c253cSXin Liif __name__ == "__main__": 456*760c253cSXin Li unittest.main() 457