Package: geneticalgorithm2
geneticalgorithm2(fromDPEA)is the supported advanced optimized fork of non-supported packagegeneticalgorithmofRyan (Mohammad) SolgiAboutInstallationUpdates informationFuture6.8.6 minor update6.8.5 minor update6.8.4 minor update6.8.3 types update6.8.2 patch6.8.1 patch6.8.0 minor update6.7.7 refactor6.7.6 bug fix6.7.5 refactor6.7.4 bug fix6.7.3 speed up6.7.2 little update6.7.1 patch6.7.0 minor update (new features)6.6.2 patch (speed up)6.6.1 patch6.6.0 minor update (refactoring)6.5.1 patch6.5.0 minor update (refactoring)6.4.1 patch (bug fix)6.4.0 minor update (refactoring)6.3.0 minor update (refactoring)Working processMain algorithm structureHow to runConstructor parametersGenetic algorithm's parametersAlgorithmParams objectParameters of algorithmCount parametersCrossoverMutationSelectionMethods and Properties of model:Examples for beginnerA minimal exampleThe simple example with integer variablesThe simple example with Boolean variablesThe simple example with mixed variablesOptimization problems with constraintsMiddle example: select fixed count of objects from setU should know these featuresAvailable crossoversFunction timeoutStandard GA vs. Elitist GAStandard crossover vs. stud EA crossoverCreating better start populationSelect best N of kNDo local optimizationOptimization with oppositionsRevolutionsDuplicates removingCacheReport checkerMiddle callbacksHow to compare efficiency of several versions of GA optimizationHints on how to adjust genetic algorithm's parameters (fromgeneticalgorithmpackage)How to get maximum speedDon't use plottingDon't use progress barTry to use faster optimizing functionSpecify custom optimizedmutation,crossover,selectionSpecifyfill_childrenmethodExamples pretty collectionOptimization test functionsSphereAckleyAckleyTestRosenbrockFletcherGriewankPenalty2QuarticRastriginSchwefelDoubleSchwefelMaxSchwefelAbsSchwefelSinStairsAbsMichalewiczSchefferEggholderWeierstrassUsing GA in reinforcement learningUsing GA with image reconstruction by polygonsPopular questionsHow to disable autoplot?How to plot population scores?How to specify evaluated function for all population?What about parallelism?How to initialize start population? How to continue optimization with new run?Aboutgeneticalgorithm2is very flexible and highly optimized Python library for implementing classicgenetic-algorithm(GA).Features of this package:written onpure pythonextremely fastno hard dependencies(only numpy primary, can work without matplotlib)easy to run: no need to perform long task-setup processeasy to logging, reachsupport of flexible callbacksmany built-in plotting functionsmany built-in cases of crossover, mutation and selectionsupport of integer, boolean and real (continuous/discrete) variables typessupport of mixed types of variablessupport of classic, elitist and studEA genetic algorithm combinationssupport of revolutions and duplicates utilizationreach support of customizationInstallationInstall this package with standard dependencies to use the entire functional.pip install geneticalgorithm2Install this package with full dependencies to use all provided functional.pip install geneticalgorithm2[full]Updates informationFutureduplicates removing and revolutions will be moved toMiddleCallbacksand removed as alonerun()parametersfunction_timeoutandfunctionwill be moved torun()methodnew stop criteria callbacks (min std, max functions evaluations)vartypewill support strings likeiiiiibbf6.8.6 minor updatesmall package installation update: addpip install geneticalgorithm2[full]versionsmall refactor6.8.5 minor updateupdateOppOpPopInit2.0.0->2.0.1set defaultfunction_timeouttoNonewhich means no use of function time checkingremovejoblibandfunc_timeoutfrom necessary dependencies6.8.4 minor updatea bit of refactorlittle optimizationsadd empty fieldfill_children(pop_matrix, parents_count)togeneticalgorithm2class to specify children creating behavior (what is the most intensive part of algorithm after optimizing func calculations), seethis6.8.3 types updatemuch more type hints6.8.2 patchfor printing infofix logic: now population is always sorted before going to callbacks6.8.1 patchprinting progress bar to'stderr'or'stdout'orNone(disable) by choice (progress_bar_streamargument ofrun()), deprecateddisable_progress_barlittle speed upnewgeneticalgorithm2.vectorized_set_functionset function, which can be faster for big populations6.8.0 minor updateremovecrossover_probabilitymodel parameter because of it has no sense to exist (and 1.0 value is better than others, take a look atresults). This parameter came fromgeneticalgorithmold package and did`t change before.6.7.7 refactorchange some behavior about parents selection6.7.6 bug fixfix some bug ofvariable_type=='bool'some refactor of progress baradd some dependencies tosetup.py6.7.5 refactorshorter progress bar (length can be controlled by settingPROGRESS_BAR_LENfield ofgeneticalgorithm2class)shorter logic ofrun(), more informative output6.7.4 bug fixbug fix6.7.3 speed uprefactor to makerun()method faster6.7.2 little updatebetter flexible logic for report,take a lookremovedshow meanparameter frommodel.plot_resultand now model reports only best score by default, not average and so on (u can specify if u wanna report average too)plot_several_linesuseful function6.7.1 patchchanges according to newOppOpPopInitversion6.7.0 minor update (new features)addmutation_discrete_typeandmutation_discrete_probabilityparameters in model. It controls mutation behavior for discrete (integer) variables and works likemutation_typeandmutation_probabilitywork for continuous (real) variables. Take a look atalgorithm parameters6.6.2 patch (speed up)fix and speed up mutation6.6.1 patchremoved unnecessary dependencies6.6.0 minor update (refactoring)deprecatedvariable_type_mixed, now usevariable_typefor mixed optimization toodeprecatedoutput_dict, now it's better object with nameresultrefactor of big part oftestsrefactor of README6.5.1 patchreplacecollections.Sequencewithcollections.abc.Sequence, now it should work forpython3.10+6.5.0 minor update (refactoring)another form of data object using with middle callbacks (MiddleCallbackDatadataclass instead of dictionary)type hints for callbacks module6.4.1 patch (bug fix)fix bug setting attribute to algorithm parameters (in middle callbacks)6.4.0 minor update (refactoring)new valid forms forstart_generation; now it's valid to useNonestrpath to saved generationdictionary with structure{'variables': variables/None, 'scores': scores/None}Generationobject:Generation(variables = variables, scores = scores)np.ndarraywith shape(samples, dim)for only population or(samples, dim+1)for concatenated population and score (scores is the last matrix column)tuple(np.ndarray/None, np.ndarray/None)for variables and scoresherevariablesis 2D numpy array with shape(samples, dim),scoresis 1D numpy array with scores (function values) for each sample;hereandhereu can see examples of using these valid forms6.3.0 minor update (refactoring)type hints for entire part of functionsnew valid forms for function parameters (now u don't need to use numpy arrays everywhere)AlgorithmParamsclass for base GA algorithm parameters (instead of dictionary)Generationclass for saving/loading/returning generation (instead of dictionary)All that classes are collectedin file. To maintain backward compatibility,AlgorithmParamsandGenerationclasses have dictionary-like interface for getting fields: u can useobject.fieldorobject['field']notations.Working processMain algorithm structurePre-process: making inner functions depends on params, making/loading start population

while True:

    if reason to stop (time is elapsed / no progress / generation count is reached / min value is reached):
        break


    select parents to crossover from last population and to put them to new population:
        select (elit count) best samples
        select (parents count - elit count) random samples (by selection function)

    create (total samples count - parents count) children (samples from selected parents) and put them to new population:
        while not all children are created:
            select 2 random parents
            make child1, child2 from them using crossover
            mutate child1 by mutation (model.mut)
            mutate child2 by middle mutation (model.mut_middle)
            put children to new population
    
    remove duplicates, make revolutions, sort population by scores
    use callbacks, use middle callbacks

Post-process: plotting results, savingHow to runFirstly, u shouldimport needed packages. All available (but not always necessary) imports are:importnumpyasnpfromgeneticalgorithm2importgeneticalgorithm2asga# for creating and running optimization modelfromgeneticalgorithm2importGeneration,AlgorithmParams,MiddleCallbackData# classes for comfortable parameters setting and gettingfromgeneticalgorithm2importCrossover,Mutations,Selection# classes for specific mutation and crossover behaviorfromgeneticalgorithm2importPopulation_initializer# for creating better start populationfromgeneticalgorithm2importnp_lru_cache# for cache function (if u want)fromgeneticalgorithm2importplot_pop_scores# for plotting population scores, if u wantfromgeneticalgorithm2importCallbacks# simple callbacks (will be deprecated)fromgeneticalgorithm2importActions,ActionConditions,MiddleCallbacks# middle callbacksNext step:define minimized functionlikedeffunction(X:np.ndarray)->float:# X as 1d-numpy arrayreturnnp.sum(X**2)+X.mean()+X.min()+X[0]*X[2]# some float resultIf u want to findmaximum, use this idea:f_tmp=lambdaarr:-target(arr)## ... find global min#target_result=-global_minOkay, also u shouldcreate the bounds for each variable(if exist) like here:var_bound=np.array([[0,10]]*3)# 2D numpy array with shape (dim, 2)# also u can use Sequence of Tuples (from version 6.3.0)var_bound=[(0,10),(0,10),(0,10)]U don't need to use variable boundaries only if variable type of each variable is boolean.After thatcreate ageneticalgorithm2(was importing as ga) object:# style before 6.3.0 version (but still works)model=ga(function,dimension=3,variable_type='real',variable_boundaries=var_bound,function_timeout=10,algorithm_parameters={'max_num_iteration':None,'population_size':100,'mutation_probability':0.1,'mutation_discrete_probability':None,'elit_ratio':0.01,'parents_portion':0.3,'crossover_type':'uniform','mutation_type':'uniform_by_center','mutation_discrete_type':'uniform_discrete','selection_type':'roulette','max_iteration_without_improv':None})# from version 6.3.0 it is equal tomodel=ga(function,dimension=3,variable_type='real',variable_boundaries=var_bound,function_timeout=10,algorithm_parameters=AlgorithmParams(max_num_iteration=None,population_size=100,mutation_probability=0.1,mutation_discrete_probability=None,elit_ratio=0.01,parents_portion=0.3,crossover_type='uniform',mutation_type='uniform_by_center',mutation_discrete_type='uniform_discrete',selection_type='roulette',max_iteration_without_improv=None))# ormodel=ga(function,dimension=3,variable_type='real',variable_boundaries=var_bound,function_timeout=10,algorithm_parameters=AlgorithmParams())Run the search method:# all of this parameters are defaultresult=model.run(no_plot=False,progress_bar_stream='stdout',disable_printing=False,set_function=None,apply_function_to_parents=False,start_generation=None,studEA=False,mutation_indexes=None,init_creator=None,init_oppositors=None,duplicates_oppositor=None,remove_duplicates_generation_step=None,revolution_oppositor=None,revolution_after_stagnation_step=None,revolution_part=0.3,population_initializer=Population_initializer(select_best_of=1,local_optimization_step='never',local_optimizer=None),stop_when_reached=None,callbacks=[],middle_callbacks=[],time_limit_secs=None,save_last_generation_as=None,seed=None)# best solutionprint(result.variable)# best scoreprint(result.score)# last populationprint(result.last_population)Constructor parametersfunction(Callable[[np.ndarray], float]) - the given objective function to be minimizedNOTE: This implementation minimizes the given objective function. (For maximization multiply function by a negative sign: the absolute value of the output would be the actual objective function)dimension(int) - the number of decision variablesvariable_type(Union[str, Sequence[str]]) - 'bool' if all variables are Boolean; 'int' if all variables are integer; and 'real' if all variables are real value or continuous. For mixed types use sequence of string of type for each variablevariable_boundaries(Optional[Union[np.ndarray, Sequence[Tuple[float, float]]]]) - Default None; leave it None if variable_type is 'bool'; otherwise provide an sequence of tuples of length two as boundaries for each variable; the length of the array must be equal dimension.
For example,np.array([[0,100],[0,200]])or[(0, 100), (0, 200)]determines lower boundary 0 and upper boundary 100 for first and upper boundary 200 for second variable where dimension is 2.function_timeout(float) - if the given function does not provide
output before function_timeout (unit is seconds) the algorithm raise error.
For example, when there is an infinite loop in the given function.Nonemeans disablingalgorithm_parameters(Union[AlgorithmParams, Dict[str, Any]]). Dictionary or AlgorithmParams object with fields:@max_num_iteration(int/None) - stopping criteria of the genetic algorithm (GA)@population_size(int > 0)@mutation_probability(float in [0,1])@mutation_discrete_probability(float in [0,1]orNone)@elit_ration(float in [0,1]) - part of elit objects in population; if > 0, there always will be 1 elit object at least@parents_portion(float in [0,1]) - part of parents from previous population to save in next population (includingelit_ration)@crossover_type(Union[str, Callable[[np.ndarray, np.ndarray], Tuple[np.ndarray, np.ndarray]]]) - Default isuniform.@mutation_type(Union[str, Callable[[float, float, float], float]]) - Default isuniform_by_center@mutation_discrete_type(Union[str, Callable[[int, int, int], int]]) - Default isuniform_discrete@selection_type(Union[str, Callable[[np.ndarray, int], np.ndarray]]) - Default isroulette@max_iteration_without_improv(int/None) - maximum number of successive iterations without improvement. IfNoneit is ineffectiveGenetic algorithm's parametersAlgorithmParams objectThe parameters of GA is defined as a dictionary orAlgorithmParamsobject:algorithm_param=AlgorithmParams(max_num_iteration=None,population_size=100,mutation_probability=0.1,mutation_discrete_probability=None,elit_ratio=0.01,parents_portion=0.3,crossover_type='uniform',mutation_type='uniform_by_center',mutation_discrete_type='uniform_discrete',selection_type='roulette',max_iteration_without_improv=None)# old style with dictionary# sometimes it's easier to use this style# especially if u need to set only few paramsalgorithm_param={'max_num_iteration':None,'population_size':100,'mutation_probability':0.1,'mutation_discrete_probability':None,'elit_ratio':0.01,'parents_portion':0.3,'crossover_type':'uniform','mutation_type':'uniform_by_center','mutation_discrete_type':'uniform_discrete','selection_type':'roulette','max_iteration_without_improv':None}To get actual default params use code:params=ga.default_paramsTo get actual parameters of existing model use code:params=model.paramAn example of setting a new set of parameters for genetic algorithm and runninggeneticalgorithm2for our first simple example again:importnumpyasnpfromgeneticalgorithm2importgeneticalgorithm2asgadeff(X):returnnp.sum(X)varbound=[(0,10)]*3algorithm_param={'max_num_iteration':3000,'population_size':100,'mutation_probability':0.1,'mutation_discrete_probability':None,'elit_ratio':0.01,'parents_portion':0.3,'crossover_type':'uniform','mutation_type':'uniform_by_center','mutation_discrete_type':'uniform_discrete','selection_type':'roulette','max_iteration_without_improv':None}model=ga(function=f,dimension=3,variable_type='real',variable_boundaries=varbound,algorithm_parameters=algorithm_param)model.run()Important. U may use the small dictionary with only important parameters; other parameters will be default. It means the dictionaryalgorithm_param={'max_num_iteration':150,'population_size':1000}is equal to:algorithm_param={'max_num_iteration':150,'population_size':1000,'mutation_probability':0.1,'mutation_discrete_probability':None,'elit_ratio':0.01,'parents_portion':0.3,'crossover_type':'uniform','mutation_type':'uniform_by_center','mutation_discrete_type':'uniform_discrete','selection_type':'roulette','max_iteration_without_improv':None}But it is better to useAlgorithmParamsobject instead of dictionaries.Parameters of algorithmCount parametersmax_num_iteration: The termination criterion of GA.
If this parameter's value isNonethe algorithm sets maximum number of iterations automatically as a function of the dimension, boundaries, and population size. The user may enter any number of iterations that they want. It is highly recommended that the user themselves determines themax_num_iterationsand not to useNone. Notice thatmax_num_iterationhas been changed to 3000 (it was alreadyNone).population_size: determines the number of trial solutions in each iteration.elit_ration: determines the number of elites in the population. The default value is 0.01 (i.e. 1 percent). For example when population size is 100 andelit_ratiois 0.01 then there is one elite unit in the population. If this parameter is set to be zero thengeneticalgorithm2implements a standard genetic algorithm instead of elitist GA.See exampleof differenceparents_portion: the portion of population filled by the members of the previous generation (aka parents); default is 0.3 (i.e. 30 percent of population)max_iteration_without_improv: if the algorithms does not improve the objective function over the number of successive iterations determined by this parameter, then GA stops and report the best found solution before themax_num_iterationsto be met. The default value isNone.Crossovercrossover_type: there are several options including'one_point','two_point','uniform','segment','shuffle'crossover functions; default is'uniform'crossover. U also can use crossover as functions fromCrossoverclass:Crossover.one_point()Crossover.two_point()Crossover.uniform()Crossover.uniform_window(window = 7)Crossover.shuffle()Crossover.segment()Crossover.mixed(alpha = 0.5)-- only for real variablesCrossover.arithmetic()-- only for real variablesHave a look atcrossovers' logicIf u want, write your own crossover function using simple syntax:defmy_crossover(parent_a:np.ndarray,parent_b:np.ndarray):# some codereturnchild_1,child_2Mutationmutation_probability: determines the chance of each gene in each individual solution to be replaced by a random value. Works for continuous variables or for all variables ifmutation_discrete_probabilityisNonemutation_discrete_probability: works likemutation_probabilitybut for discrete variables. IfNone, will be changed tomutation_probabilityvalue; so just don't specify this parameter if u don't need special mutation behavior for discrete variablesmutation_type: there are several options (only for real variables) including'uniform_by_x','uniform_by_center','gauss_by_x','gauss_by_center'; default is'uniform_by_center'. U also can use mutation as functions fromMutationsclass:Mutations.gauss_by_center(sd = 0.2)Mutations.gauss_by_x(sd = 0.1)Mutations.uniform_by_center()Mutations.uniform_by_x()(If u want) write your mutation function using syntax:defmy_mutation(current_value:float,left_border:float,right_border:float)->float:# some codereturnnew_valuemutation_discrete_type: now there is only one option for discrete variables mutation:uniform_discrete(Mutations.uniform_discrete()) which works likeuniform_by_centerreal mutation but with integer numbers. Anyway, this option was included at version 6.7.0 to support custom discrete mutations if u need it. For using custom mutation just set this parameter to function likedefmy_mutation(current_value:int,left_border:int,right_border:int)->int:# some codereturnnew_valueSelectionselection_type: there are several options (only for real) including'fully_random'(just for fun),'roulette','stochastic','sigma_scaling','ranking','linear_ranking','tournament'; default isroulette. U also can use selection as functions fromSelectionclass:Selection.fully_random()Selection.roulette()Selection.stochastic()Selection.sigma_scaling(epsilon = 0.05)Selection.ranking()Selection.linear_ranking(selection_pressure = 1.5)Selection.tournament(tau = 2)If u want, write your selection function using syntax:defmy_mutation(sorted_scores:np.ndarray,parents_count:int):# some codereturnarray_of_parents_indexesMethods and Properties of model:The main method ifrun(). It has parameters:no_plot(bool) - do not plot results using matplotlib by defaultprogress_bar_stream(Optional[str]) -'stdout'to print progress bar tostdout,'stderr'forstderr,Noneto disable progress bar (also it can be faster by 10-20 seconds)disable_printing(bool) - don't print any text (except progress bar)set_function(Optional[Callable[[np.ndarray], np.ndarray]]): 2D-array -> 1D-array function, which applies to matrix of population (size (samples, dimension)) to estimate their values ("scores" in some sense)apply_function_to_parents(bool) - apply function to parents from previous generation (if it's needed), it can be needed at working with games agents, but for other tasks will just waste timestart_generation(Union[str, Dict[str, np.ndarray], Generation, np.ndarray, Tuple[Optional[np.ndarray], Optional[np.ndarray]]]) -- one of cases (take a look):Generationobjectdictionary with structure{'variables':2D-array of samples, 'scores': function values on samples}(if'scores'value isNonethe scores will be compute)path to.npzfile (str) with saved generationnp.ndarray(with shape(samples, dim)or(samples, dim+1))tuple ofnp.ndarrays /None.studEA(bool) - using stud EA strategy (crossover with best object always). Default is false.Take a lookmutation_indexes(Optional[Union[Sequence[int], Set[int]]]) - indexes of dimensions where mutation can be performed (all dimensions by default).Exampleinit_creator: (Optional[Callable[[], np.ndarray]]), the function creates population samples. By default -- random uniform for real variables and random uniform for int.Exampleinit_oppositors: (Optional[Sequence[Callable[[np.ndarray], np.ndarray]]]) -- the list of oppositors creates oppositions for base population. No by default.Exampleduplicates_oppositor:Optional[Callable[[np.ndarray], np.ndarray]], oppositor for applying after duplicates removing. By default -- using just random initializer from creator.Exampleremove_duplicates_generation_step:None/int, step for removing duplicates (have a sense with discrete tasks). No by default.Examplerevolution_oppositor=Optional[Callable[[np.ndarray], np.ndarray]], oppositor for revolution time. No by default.Examplerevolution_after_stagnation_step=None/int, create revolution after this generations of stagnation. No by default.Examplerevolution_part(float): the part of generation to being oppose. By default is 0.3.Examplepopulation_initializer(Tuple[int, Callable[[np.ndarray, np.ndarray], Tuple[np.ndarray, np.ndarray]]]) -- object for actions at population initialization step to create better start population.Take a lookstop_when_reached(Optional[float]) -- stop searching after reaching this value (it can be potential minimum or something else)callbacks(Optional[Sequence[Callable[[int, List[float],  np.ndarray, np.ndarray], None]]]) - list of callback functions with structure:defcallback(generation_number,report_list,last_population_as_2D_array,last_population_scores_as_1D_array):## do some action#Seeexample of using callbacks. There are several callbacks inCallbacksclass, such as:Callbacks.SavePopulation(folder, save_gen_step = 50, file_prefix = 'population')Callbacks.PlotOptimizationProcess(folder, save_gen_step = 50, show = False, main_color = 'green', file_prefix = 'report')middle_callbacks(Sequence) - list of functions madeMiddleCallbacksclass (large opportunity, please, have a look atthis)time_limit_secs(Optional[float]) - limit time of working (in seconds). IfNone, there is no time limit (limit only for count of generation and so on). Seelittle example of using. Also there is simple conversion function for conversion some time in seconds:fromtruefalsepythonimporttime_to_secondstotal_seconds=time_to_seconds(days=2,# 2 dayshours=13,# plus 13 hoursminutes=7,# plus 7 minutesseconds=44# plus 44 seconds)save_last_generation_as(Optional[str]) - path to.npzfile for saving last_generation as numpy dictionary like{'population': 2D-array, 'scores': 1D-array},Noneif doesn't need to save in file;take a lookseed(Optional[int]) - random seed (None is doesn't matter)It would be more logical to use params likestudEAas an algorithm param, butrun()-way can be more comfortable for real using.output:result: is a wrap on last generation with fields:last_generation--Generationobject of last generationvariable-- best unit of last generationscore-- metric of the best unitreport: is a record of the progress of the algorithm over iterations. Also u can specify to report not only best values.Go toExamples for beginnerA minimal exampleAssume we want to find a set ofX = (x1,x2,x3)that minimizes functionf(X) = x1 + x2 + x3whereXcan be any real number in[0, 10].This is a trivial problem and we already know that the answer isX = (0,0,0)wheref(X) = 0.We just use this simple example to see how to implement geneticalgorithm2. First we import geneticalgorithm2 andnumpy. Next, we define
functionfwhich we want to minimize and the boundaries of the decision variables. Then simply geneticalgorithm2 is called to solve the defined optimization problem as follows:importnumpyasnpfromgeneticalgorithm2importgeneticalgorithm2asgadeff(X):returnnp.sum(X)varbound=[[0,10]]*3model=ga(function=f,dimension=3,variable_type='real',variable_boundaries=varbound)model.run()geneticalgorithm2 has some arguments:Obviously the first argument is the functionfwe already defined.Our problem has three variables so we set dimension equal3.Variables are real (continuous) so we use string'real'to notify the type of
variables (geneticalgorithm2 accepts other types including boolean, integers and
mixed; see other examples).Finally, we inputvarboundwhich includes the boundaries of the variables.
Note that the length of variable_boundaries must be equal to dimension.If you run the code, you should see a progress bar that shows the progress of the
genetic algorithm (GA) and then the solution, objective function value and the convergence curve as follows:Also we can access to the best answer of the defined optimization problem found by GA as a dictionary and a report of the progress of the genetic algorithm.
To do so we complete the code as follows:convergence=model.reportsolution=model.resultThe simple example with integer variablesConsidering the problem given in the simple example above.
Now assume all variables are integers. Sox1, x2, x3can be any integers in[0, 10].
In this case the code is as the following:importnumpyasnpfromgeneticalgorithm2importgeneticalgorithm2asgadeff(X):returnnp.sum(X)varbound=[[0,10]]*3model=ga(function=f,dimension=3,variable_type='int',variable_boundaries=varbound)model.run()So, as it is seen the only difference is that forvariable_typewe use string'int'.The simple example with Boolean variablesConsidering the problem given in the simple example above.
Now assume all variables are boolean instead of real or integer. SoXcan be either zero or one. Also instead of three let's have 30 variables.
In this case the code is as the following:importnumpyasnpfromgeneticalgorithm2importgeneticalgorithm2asgadeff(X):returnnp.sum(X)model=ga(function=f,dimension=30,variable_type='bool')model.run()Note for variable_type we use string'bool'when all variables are boolean.Note that when variable_type equal'bool'there is no need forvariable_boundariesto be defined.The simple example with mixed variablesConsidering the problem given in the the simple example above where we want to minimizef(X) = x1 + x2 + x3.
Now assumex1is a real (continuous) variable in[0.5,1.5],x2is an integer variable in[1,100], andx3is a boolean variable that can be either zero or one.
We already know that the answer isX = (0.5,1,0)wheref(X) = 1.5We implement geneticalgorithm2 as the following:importnumpyasnpfromgeneticalgorithm2importgeneticalgorithm2asgadeff(X):returnnp.sum(X)varbound=[[0.5,1.5],[1,100],[0,1]]vartype=('real','int','int')model=ga(function=f,dimension=3,variable_type=vartype,variable_boundaries=varbound)model.run()Optimization problems with constraintsIn all above examples, the optimization problem was unconstrained. Now consider that we want to minimizef(X) = x1+x2+x3whereXis a set of real variables in[0, 10]. Also we have an extra constraint so that sum ofx1andx2is equal or greater than 2. The minimum off(X)is 2.
In such a case, a trick is to define penalty function. Hence we use the code below:importnumpyasnpfromgeneticalgorithm2importgeneticalgorithm2asgadeff(X):pen=0ifX[0]+X[1]<2:pen=500+1000*(2-X[0]-X[1])returnnp.sum(X)+penvarbound=[[0,10]]*3model=ga(function=f,dimension=3,variable_type='real',variable_boundaries=varbound)model.run()As seen above we add a penalty to the objective function whenever the constraint is not met.Some hints about how to define a penalty function:Usually you may use a constant greater than the maximum possible value of the objective function if the maximum is known or if we have a guess of that. Here the highest possible value of our function is 300 (i.e. if all variables were 10,f(X)=300). So I chose a constant of 500. So, if a trial solution is not in the feasible region even though its objective function may be small, the penalized objective function (fitness function) is worse than any feasible solution.Use a coefficient big enough and multiply that by the amount of violation. This helps the algorithm learn how to approach feasible domain.How to define penalty function usually influences the convergence rate of an evolutionary algorithm. In mybook on metaheuristics and evolutionary algorithmsyou can learn more about that.Finally after you solved the problem test the solution to see if boundaries are met. If the solution does not meet constraints, it shows that a bigger penalty is required. However, in problems where optimum is exactly on the boundary of the feasible region (or very close to the constraints) which is common in some kinds of problems, a very strict and big penalty may prevent the genetic algorithm to approach the optimal region. In such a case designing an appropriate penalty function might be more challenging. Actually what we have to do is to design a penalty function that let the algorithm searches unfeasible domain while finally converge to a feasible solution. Hence you may need more sophisticated penalty functions. But in most cases the above formulation work fairly well.Middle example: select fixed count of objects from setFor some task u need to think a lot and create good specific crossover or mutation functions. For example, take a look at this problem:From set like X = {x1, x2, x3, ..., xn} u should select only k objects which get the best function valueU can do it using this code:importnumpyasnpfromgeneticalgorithm2importgeneticalgorithm2asgasubset_size=20# how many objects we can chooseobjects_count=100# how many objects are in setmy_set=np.random.random(objects_count)*10-5# set values# minimized functiondeff(X):returnabs(np.mean(my_set[X==1])-np.median(my_set[X==1]))# initialize start generation and paramsN=1000# size of populationstart_generation=np.zeros((N,objects_count))indexes=np.arange(0,objects_count,dtype=np.int8)# indexes of variablesforiinrange(N):inds=np.random.choice(indexes,subset_size,replace=False)start_generation[i,inds]=1defmy_crossover(parent_a,parent_b):a_indexes=set(indexes[parent_a==1])b_indexes=set(indexes[parent_b==1])intersect=a_indexes.intersection(b_indexes)# elements in both parentsa_only=a_indexes-intersect# elements only in 'a' parentb_only=b_indexes-intersectchild_inds=np.array(list(a_only)+list(b_only),dtype=np.int8)np.random.shuffle(child_inds)# mixchildren=np.zeros((2,parent_a.size))ifintersect:children[:,np.array(list(intersect))]=1children[0,child_inds[:int(child_inds.size/2)]]=1children[1,child_inds[int(child_inds.size/2):]]=1returnchildren[0,:],children[1,:]model=ga(function=f,dimension=objects_count,variable_type='bool',algorithm_parameters={'max_num_iteration':500,'mutation_probability':0,# no mutation, just crossover'elit_ratio':0.05,'parents_portion':0.3,'crossover_type':my_crossover,'max_iteration_without_improv':20})model.run(no_plot=False,start_generation=(start_generation,None))U should know these featuresAvailable crossoversFor two example parents (one with onesandone with zeros) next crossovers will give same children (examples):one_point:000000001111111111111110000000two_point:110000000000111001111111111000uniform:111011000001000000100111110111uniform_window:111111111000111000000000111000shuffle:000111100111010111000011000101segment:011001010010011100110101101100arithmetic:0.130.130.130.130.130.130.130.130.130.130.130.130.130.130.130.870.870.870.870.870.870.870.870.870.870.870.870.870.870.87mixed:0.630.841.10.730.67-0.190.30.72-0.180.610.841.141.36-0.37-0.190.510.580.430.420.550.490.570.480.460.560.560.540.440.510.4Function timeoutgeneticalgorithm2is designed such that if the given function does not provide
any output before timeout (the default value is 10 seconds), the algorithm
would be terminated and raise the appropriate error.In such a case make sure the given function
works correctly (i.e. there is no infinite loop in the given function). Also if the given function takes more than 10 seconds to complete the work
make sure to increase function_timeout in arguments.Standard GA vs. Elitist GAThe convergence curve of an elitist genetic algorithm is always non-increasing. So, the best ever found solution is equal to the best solution of the last iteration. However, the convergence curve of a standard genetic algorithm is different. Ifelit_ratiois zero geneticalgorithm2 implements a standard GA. The output of geneticalgorithm2 for standard GA is the best ever found solution not the solution of the last iteration. The difference between the convergence curve of standard GA and elitist GA is shown below:Standard crossover vs. stud EA crossoverStud EAis the idea of using crossover always with best object. So one of two parents is always the best object of population. It can help us in a lot of tasks!Creating better start populationThere isPopulation_initializer(select_best_of = 4, local_optimization_step = 'never', local_optimizer = None)object for creating better start population. It has next arguments:select_best_of(int) -- select1/select_best_ofbest part of start population. For example, forselect_best_of = 4andpopulation_size = Nwill be selected N best objects from 5N generated objects (ifstart_generation = None). Ifstart_generationis not None, it will be selected bestsize(start_generation)/Nobjectslocal_optimization_step(str) -- when should we do local optimization? Available values:'never'-- don't do local optimization'before_select'-- before selection best N objects (example: do local optimization for 5N objects and select N best results)'after_select'-- do local optimization on best selected N objectslocal_optimizer(function) -- local optimization function like:defloc_opt(object_as_array,current_score):# some codereturnbetter_object_as_array,better_scoreSelect best N of kNThis little option can help u especially with multimodal tasks.Do local optimizationWe can apply some local optimization on start generation before starting GA search. It can be some gradient descent or hill climbing and so on. Also we can apply it before selection best objects (on entire population) or after (on best part of population) and so forth.In next example I'm using myDiscreteHillClimbingalgorithm for local optimization my discrete task:importnumpyasnpimportmatplotlib.pyplotaspltfromDiscreteHillClimbingimportHill_Climbing_descentfromgeneticalgorithm2importgeneticalgorithm2asgafromgeneticalgorithm2importPopulation_initializerdeff(arr):arr2=arr/25return-np.sum(arr2*np.sin(np.sqrt(np.abs(arr2))))**5+np.sum(np.abs(arr2))**2iterations=100varbound=[[-100,100]]*15available_values=[np.arange(-100,101)]*15my_local_optimizer=lambdaarr,score:Hill_Climbing_descent(function=f,available_predictors_values=available_values,max_function_evals=50,start_solution=arr)model=ga(function=f,dimension=varbound.shape[0],variable_type='int',variable_boundaries=varbound,algorithm_parameters={'max_num_iteration':iterations,'population_size':400})fortimein('before_select','after_select','never'):model.run(no_plot=True,population_initializer=Population_initializer(select_best_of=3,local_optimization_step=time,local_optimizer=my_local_optimizer))plt.plot(model.report,label=f"local optimization time = '{time}'")plt.xlabel('Generation')plt.ylabel('Minimized function (40 simulations average)')plt.title('Selection best N object before running GA')plt.legend()Optimization with oppositionsAlso u can create start population withoppositions. Seeexample of codeRevolutionsU can createrevolutions in your populationafter some stagnation steps. It really can help u for some tasks. SeeexampleDuplicates removingIf u remove duplicates eachkgenerations, u can speed up the optimization process (example)CacheIt can be useful for run-speed to use cache withsome discrete tasks. For this u can importnp_lru_cachedecorator and use it like here:importnp_lru_cache@np_lru_cache(maxsize=some_size)defminimized_func(arr):# codereturnresult## run#    algorithm## don't forget to clear cacheminimized_func.cache_clear()Report checkerBasically the model checks best population score (minimal score of generation) each generation and saves it toreportfield. Actually this sequence of numbers u see in big part of plots. This behavior is needed for several parts and u cannot disable it. But if u want to report some other metric without usingcallbacks, there is highly simple and fast way.After creatingmodelbut before runningrun()u need to append ur logic tomodel.checked_reportsfield. Take a look at example:importnumpyasnpfromgeneticalgorithm2importgeneticalgorithm2asgafromgeneticalgorithm2importplot_several_linesdeff(X):return50*np.sum(X)-np.sum(np.sqrt(X)*np.sin(X))dim=25varbound=[[0,10]]*dimmodel=ga(function=f,dimension=dim,variable_type='real',variable_boundaries=varbound,algorithm_parameters={'max_num_iteration':600})# here model exists and has checked_reports field# now u can append any functions to reportmodel.checked_reports.extend([('report_average',np.mean),('report_25',lambdaarr:np.quantile(arr,0.25)),('report_50',np.median)])# run optimization processmodel.run(no_plot=False)# now u have not only model.report but model.report_25 and so on#plot reportsnames=[nameforname,_inmodel.checked_reports[::-1]]plot_several_lines(lines=[getattr(model,name)fornameinnames],colors=('green','black','red','blue'),labels=['median value','25% quantile','mean of population','best pop score'],linewidths=(1,1.5,1,2),title="Several custom reports with base reports",save_as='./output/report.png')As u see, u should append tuple(name of report, func to evaluate report)tomodel.checked_report. It's highly recommended to start this name withreport_(e. g.report_my_median). And the function u use will get 1D-numpysortedarray of population scores.Middle callbacksThere is an amazing way to control optimization process usingMiddleCallbacksclass. Just learn next logic:u can use severalMiddleCallbackscallbacks as list atmiddle_callbacksparameter inrun()methodeach middle callback is the pair ofactionandconditionfunctionscondition(data)(Callable[[MiddleCallbackData], bool]) function getsdataobject (dataclassMiddleCallbackDatafrom version 6.5.0) about primary model parameters and makes logical decision about applyingactionfunctionaction(data)(Callable[[MiddleCallbackData],MiddleCallbackData]) function modifiesdataobjects as u need -- and model will be modified by newdatadataobject is the structure with several parameters u can modify:data=MiddleCallbackData(last_generation=Generation.from_pop_matrix(pop),current_generation=t,report_list=self.report,mutation_prob=self.prob_mut,crossover_prob=self.prob_cross,mutation=self.real_mutation,crossover=self.crossover,selection=self.selection,current_stagnation=counter,max_stagnation=self.max_stagnations,parents_portion=self.param.parents_portion,elit_ratio=self.param.elit_ratio,set_function=self.set_function)So, theactionfunction getsdataobjects and returnsdataobject.It's very simple to create your ownactionandconditionfunctions. But there are several popular functions contained inActionsandActionConditionsclasses:actions:Stop()-- just stop optimization processReduceMutationProb(reduce_coef = 0.9)-- reduce mutation probabilityChangeRandomCrossover(available_crossovers: Sequence[Callable[[np.ndarray, np.ndarray], Tuple[np.ndarray, np.ndarray]]])-- change another (random) crossover from list of crossoversChangeRandomSelection(available_selections: Sequence[Callable[[np.ndarray, int], np.ndarray]])ChangeRandomMutation(available_mutations: Sequence[Callable[[float, float, float], float]])RemoveDuplicates(oppositor = None, creator = None, converter = None); seedocCopyBest(by_indexes)-- copies best population object values (from dimensions inby_indexes) to all populationPlotPopulationScores(title_pattern = lambda data: f"Generation {data['current_generation']}", save_as_name_pattern = None)-- plot population scores; needs 2 functions likedata->string for title and file name (to save)conditions:ActionConditions.EachGen(generation_step = 10)-- do action eachgeneration_stepgenerationsActionConditions.Always()do action each generations, equals toActionConditions.EachGen(1)ActionConditions.AfterStagnation(stagnation_generations = 50)-- do action afterstagnation_generationsstagnation generationsActionConditions.Several(list_of_conditions)-- do action if all conditions in list are trueTo combineactionandconditionto callback, just useMiddleCallbacks.UniversalCallback(action, condition)methods.There are also next high-level useful callbacks:MiddleCallbacks.ReduceMutationGen(reduce_coef = 0.9, min_mutation = 0.005, reduce_each_generation = 50, reload_each_generation = 500)MiddleCallbacks.GeneDiversityStats(step_generations_for_plotting:int = 10)-- plots some duplicates statistics each gen (example)Seecode exampleHow to compare efficiency of several versions of GA optimizationTo compare efficiency of several versions of GA optimization (such as several values of several hyperparameters or including/excepting some actions like oppositions) u should make some count of simulations and compare results using some statistical test. I have realized this logichereHints on how to adjust genetic algorithm's parameters (fromgeneticalgorithmpackage)In general the performance of a genetic algorithm or any evolutionary algorithm
depends on its parameters. Parameter setting of an evolutionary algorithm is important. Usually these parameters are adjusted based on experience and by conducting a sensitivity analysis.
It is impossible to provide a general guideline to parameter setting but the suggestions provided below may help:Number of iterations: Select amax_num_iterationssufficiently large; otherwise the reported solution may not be satisfactory. On the other hand
selecting a very large number of iterations increases the run time significantly. So this is actually a compromise between
the accuracy you want and the time and computational cost you spend.Population size: Given a constant number of functional evaluations (max_num_iterationstimes population_size) I would select smaller population size and greater iterations. However, a very small choice of population size is also deteriorative. For most problems I would select a population size of 100 unless the dimension of the problem is very large that needs a bigger population size.elit_ratio: Although having few elites is usually a good idea and may increase the rate of convergence in some problems, having too many elites in the population may cause the algorithm to easily trap in a local optima. I would usually select only one elite in most cases. Elitism is not always necessary and in some problems may even be deteriorative.mutation_probability: This is a parameter you may need to adjust more than the other ones. Its appropriate value heavily depends on the problem. Sometimes we may select
mutation_probability as small as 0.01 (i.e. 1 percent) and sometimes even as large as 0.5 (i.e. 50 percent) or even larger. In general if the genetic algorithm trapped
in a local optimum increasing the mutation probability may help. On the other hand if the algorithm suffers from stagnation reducing the mutation probability may be effective. However, this rule of thumb is not always true.parents_portion: If parents_portion set zero, it means that the whole of the population is filled with the newly generated solutions.
On the other hand having this parameter equals 1 (i.e. 100 percent) means no new solution
is generated and the algorithm would just repeat the previous values without any change which is not meaningful and effective obviously. Anything between these two may work. The exact value depends on the problem.crossover_type: Depends on the problem. I would usually use uniform crossover. But testing the other ones in your problem is recommended.max_iteration_without_improv: This is a parameter that I recommend being used cautiously.
If this parameter is too small then the algorithm may stop while it trapped in a local optimum.
So make sure you select a sufficiently large criteria to provide enough time for the algorithm to progress and to avoid immature convergence.Finally to make sure that the parameter setting is fine, we usually should run the
algorithm for several times and if convergence curves of all runs converged to the same objective function value we may accept that solution as the optimum. The number of runs
depends but usually five or ten runs is prevalent. Notice that in some problems
several possible set of variables produces the same objective function value.
When we study the convergence of a genetic algorithm we compare the objective function values not the decision variables.How to get maximum speedDon't use plottingresult=model.run(no_plot=True,)Don't use progress barresult=model.run(progress_bar_stream=None,)Try to use faster optimizing functionTry to speed up your optimizingfunctionusing Numpy,NumbaorCython. If u can, write your ownset_function(function which applies to whole population samples matrix) with cython optimizations, parallelism and so.Specify custom optimizedmutation,crossover,selectionWrite faster implementations for model methodsmut,mut_middle,crossover,selectionand set them before running optimization process:model.mut=custom_mutmodel.crossover=custom_crossovermodel.run(...)Specifyfill_childrenmethodFrom version6.8.4there isfill_childrenmodel method:self.fill_children:Optional[Callable[[array2D,int],None]]=NoneIt is empty and does nothing; but if u specify it, u can get huge speed up at very intensive algorithm part. Take a look atmain algo structure. There is a part with creating children from parents, this part is the most intensive because it uses python loops, calls sampling, crossover and mutations at each iteration. Usingfill_children, u can rewrite this logic in your manner to speed up.Suppose u have new population matrixpop(typenp.float64, shape(population_size, dim_count)) where firstparents_countrows are selected parents, next rows are filled by random, so insidefill_childrenmethod u should fill lastpopulation_size - parents_countrows (children) by using some your logic. Expected (but not mandatory) logic like this:forkinrange(self.parents_count,self.population_size,2):r1,r2=get_parents_inds()# get 2 random parents indexes from [0, parents_count)pvar1=pop[r1]pvar2=pop[r2]ch1,ch2=self.crossover(pvar1,pvar2)# crossover# mutationsch1=self.mut(ch1)ch2=self.mut_middle(ch2,pvar1,pvar2)# put to populationpop[k]=ch1pop[k+1]=ch2Example. In one task I use this algorithm many times (100 000 generations total), so the speed matters. Every sample item is the index of element in other array there, soith sample element is always integer value from cut[0, end[i]]. I use uniform crossover and uniform mutation (work perfect for this task). So I specified creating children logic for this task using cython.Content of filefill_children.pyx:#!python#cython: language_level=3importnumpyasnpcimportnumpyasnpnp.import_array()cimportcythonimportmathimportrandom@cython.boundscheck(False)@cython.wraparound(False)deffill_children(np.ndarray[np.float64_t,ndim=2]pop,# samples are integers but always float64 typeintparents_count,# count of already done parentsfloatmut_prob,# mutation probabilitynp.ndarray[np.uint8_t,ndim=1]ends# max elements for each dimension (min elements are 0)):cdef:Py_ssize_ti,k,population_size=pop.shape[0],dim_count=pop.shape[1],r1,r2floatv1,v2,tmpnp.ndarray[np.float64_t,ndim=1]cross,mut,mut_middle# making 2 children at each iterationforkinrange(parents_count,population_size,2):# C loop, not Python## 2 random parents (fast implementation)#r1=random.randrange(parents_count)r2=random.randrange(parents_count)ifr1==r2:whiler1==r2:# C loop!r2=random.randrange(parents_count)## I always need these 3 random probs sequences, so the fastest way to obtain them is np.random.random#cross=np.random.random(dim_count)# crossover probabilities for each dimensionmut=np.random.random(dim_count)mut_middle=np.random.random(dim_count)foriinrange(dim_count):# C loop for each dimensionv1=pop[r1,i]# first parent valuev2=pop[r2,i]# second parent valueifcross[i]<0.5:# random swap (uniform crossover), copy otherwisetmp=v2v2=v1v1=tmpifmut[i]<mut_prob:# random mutation for first child# fastest way to get random integer from [0, ends[i]]# random.random() calls not always but only on mut[i] < mut_probv1=math.floor(random.random()*(ends[i]+1))ifmut_middle[i]<mut_prob:# mut_middle for secondtmp=random.random()ifv1<v2:v2=v1+math.floor(tmp*(v2-v1+1))# integer from [v1, v2], v1 < v2elifv1>v2:v2=v2+math.floor(tmp*(v1-v2+1))# integer from [v2, v1], v2 < v1else:v2=math.floor(tmp*(ends[i]+1))## put values to children in array#pop[k,i]=v1pop[k+1,i]=v2After compilation this file I can call it from python file to use inside GA:mut_prob=param['mutation_probability']deffill_children(pop:array2D,parents_count:int):"""wrapper on fill_children.fill_children with putting local variables mut_prob, ends"""returnfill_children.fill_children(pop,parents_count,mut_prob,ends)model.fill_children=fill_childrenmodel.run(...)Examples pretty collectionOptimization test functionsHere there is the implementation ofgeneticalgorithm2for some benchmark problems. Test functions are got from myOptimizationTestFunctionspackage.The code for optimizations process is same for each function and is containedin file.SphereAckleyAckleyTestRosenbrockFletcherGriewankPenalty2QuarticRastriginSchwefelDoubleSchwefelMaxSchwefelAbsSchwefelSinStairsAbsMichalewiczSchefferEggholderWeierstrassUsing GA in reinforcement learningSeeexample of using GA optimization with keras neural networksfor solving OpenGym tasks.Better example isOpenGym using cost2fitness and geneticalgorithm2where I use also mycost2fitnesspackage for fast forward propagationUsing GA with image reconstruction by polygonsLinks:https://www.kaggle.com/demetrypascal/fork-of-imagereconstruction-with-geneticalgorithm2https://www.kaggle.com/demetrypascal/imagereconstructionpolygons-with-geneticalgorithm2Popular questionsHow to disable autoplot?Just useno_plot = Trueparam inrunmethod:model.run(no_plot=True)If u want, u can plot results later by usingmodel.plot_results()Also u can create your pretty plots usingmodel.reportobject (it's a list of values):re=np.array(model.report)plt.plot(re)plt.xlabel('Iteration')plt.ylabel('Objective function')plt.title('Genetic Algorithm')plt.show()How to plot population scores?There are 2 ways to plot of scores of population:useplot_pop_scores(scores, title = 'Population scores', save_as = None)function fromgeneticalgorithm2environmentuseplot_generation_scores(self, title = 'Last generation scores', save_as = None)method ofgaobject for plotting scores of last generation (yes, it's wrapper of previous function)Let's check example:importnumpyasnpfromgeneticalgorithm2importgeneticalgorithm2asgafromgeneticalgorithm2importplot_pop_scores# for plotting scores without ga objectdeff(X):return50*np.sum(X)-np.sum(np.sqrt(X)*np.sin(X))dim=25varbound=[[0,10]]*dim# create start populationstart_pop=np.random.uniform(0,10,(50,dim))# eval scores of start populationstart_scores=np.array([f(start_pop[i])foriinrange(start_pop.shape[0])])# plot start scores using plot_pop_scores functionplot_pop_scores(start_scores,title='Population scores before beginning of searching',save_as='plot_scores_start.png')model=ga(function=f,dimension=dim,variable_type='real',variable_boundaries=varbound)# run optimization processmodel.run(no_plot=True,start_generation={'variables':start_pop,'scores':start_scores})# plot and save optimization process plotmodel.plot_results(save_as='plot_scores_process.png')# plot scores of last populationmodel.plot_generation_scores(title='Population scores after ending of searching',save_as='plot_scores_end.png')How to specify evaluated function for all population?U can do it usingset_functionparameter intorun()method.This function should getnumpy 2D-array(samples x dimension) and return1D-arraywith results.By default it usesset_function = geneticalgorithm2.default_set_function(function), wheredefdefault_set_function(function_for_set):deffunc(matrix):returnnp.array([function_for_set(matrix[i,:])foriinrange(matrix.shape[0])])returnfuncU may want to use it for creating some specific or fast-vectorized evaluations like here:defsigmoid(z):return1/(1+np.exp(-z))matrix=np.random.random((1000,100))defvectorised(X):returnsigmoid(matrix.dot(X))model.run(set_function=vectorised)What about parallelism?By usingset_functionu can determine your own behavior for parallelism or u can usegeneticalgorithm2.set_function_multiprocess(f, n_jobs = -1)for using just parallelism (recommended for heavy functions and big populations, not recommended for fast functions and small populations).For example:importnumpyasnpfromgeneticalgorithm2importgeneticalgorithm2asgadeff(X):importmatha=X[0]b=X[1]c=X[2]s=0foriinrange(10000):s+=math.sin(a*i)+math.sin(b*i)+math.cos(c*i)returnsalgorithm_param={'max_num_iteration':50,'population_size':100,'mutation_probability':0.1,'elit_ratio':0.01,'parents_portion':0.3,'crossover_type':'uniform','mutation_type':'uniform_by_center','selection_type':'roulette','max_iteration_without_improv':None}varbound=np.array([[-10,10]]*3)model=ga(function=f,dimension=3,variable_type='real',variable_boundaries=varbound,algorithm_parameters=algorithm_param)########%timemodel.run()# Wall time: 1min 52s%timemodel.run(set_function=ga.set_function_multiprocess(f,n_jobs=6))# Wall time: 31.7 sHow to initialize start population? How to continue optimization with new run?For this there isstart_generationparameter inrun()method. It's the dictionary with structure like returnedmodel.output_dict['last_generation']. Let's see example how can u to use it:importnumpyasnpfromgeneticalgorithm2importgeneticalgorithm2asgadeff(X):returnnp.sum(X)dim=6varbound=[(0,10)]*dimalgorithm_param={'max_num_iteration':500,'population_size':100,'mutation_probability':0.1,'elit_ratio':0.01,'parents_portion':0.3,'crossover_type':'uniform','max_iteration_without_improv':None}model=ga(function=f,dimension=dim,variable_type='real',variable_boundaries=varbound,algorithm_parameters=algorithm_param)# start generation# as u see u can use any values been valid for ur functionsamples=np.random.uniform(0,50,(300,dim))# 300 is the new size of your generationmodel.run(no_plot=False,start_generation={'variables':samples,'scores':None})# it's not necessary to evaluate scores before# but u can do it if u have evaluated scores and don't wanna repeat calculations# from version 6.3.0 it's recommended to use this formfromgeneticalgorithm2importGenerationmodel.run(no_plot=False,start_generation=Generation(variables=samples,scores=None))# from version 6.4.0 u also can use these formsmodel.run(no_plot=False,start_generation=samples)model.run(no_plot=False,start_generation=(samples,None))# if u have scores array, u can put it tooscores=np.array([f(sample)forsampleinsamples])model.run(no_plot=False,start_generation=(samples,scores))#### after first run## best value = 0.10426190111045064### okay, let's continue optimization using saved last generationmodel.run(no_plot=True,start_generation=model.output_dict['last_generation'])#### after second run## best value = 0.06128462776296528##Also u can save and load populations using likely code:importnumpyasnpfromgeneticalgorithm2importgeneticalgorithm2asgafromOptimizationTestFunctionsimportEggholderdim=2*15f=Eggholder(dim)xmin,xmax,ymin,ymax=f.boundsvarbound=np.array([[xmin,xmax],[ymin,ymax]]*15)model=ga(function=f,dimension=dim,variable_type='real',variable_boundaries=varbound,algorithm_parameters={'max_num_iteration':300,'population_size':100})# first run and save last generation to filefilename="eggholder_lastgen.npz"model.run(save_last_generation_as=filename)# load start generation from file and run again (continue optimization)model.run(start_generation=filename)