.. _part:userguide: Users Guide ----------- .. _chp:structure: The Structure of PGAPack ++++++++++++++++++++++++ This chapter provides a general overview of the structure of PGAPack. .. _sec:data-structure: Native Data Types ~~~~~~~~~~~~~~~~~ PGAPack is a data-structure-neutral library. By this we mean that a data-hiding capability provides the full functionality of the library to the user, in a transparent manner, irrespective of the data type used. PGAPack supports four native data types: binary-valued, integer-valued, real-valued, and character-valued strings. In addition, PGAPack is designed to be easily extended to support other data types (see Chapter :ref:`chp:custom1`). The binary (or bit) data type (i.e., ``|1|0|1|1|``) is the traditional GA coding. The bits may either be interpreted literally or decoded into integer or real values by using either binary coded decimal or binary-reflected Gray codes. In PGAPack the binary data type is implemented by using each distinct bit in a computer word as a gene, making the software very memory-efficient. The integer-valued data type (i.e., ``|3|9|2|4|``) is often used in routing and scheduling problems. The real-valued data type (i.e., ``|4.2|7.1|-6.3|0.8|``) is useful in numerical optimization applications. The character-valued data type (i.e., ``|h|e|l|l|o|w|o|r|l|d|``\ is useful for symbolic applications. .. _sec:context: Context Variable ~~~~~~~~~~~~~~~~ In PGAPack the *context variable* is the data structure that provides the data hiding capability. The context variable is a pointer to a C language structure, which is itself a collection of other structures. These (sub)structures contain all the information necessary to run the genetic algorithm, including data type specified, parameter values, which functions to call, operating system parameters, debugging flags, initialization choices, and internal scratch arrays. By hiding the actual data type selected and specific functions that operate on that data type in the context variable, user-level functions in PGAPack can be called independent of the data type. Almost all fields in the context variable have default values. However, the user can set values in the context variable by using the :ref:`PGASet ` family of function calls. The values of fields in the context variable may be read with the :ref:`PGAGet ` family of function calls. .. _sec:usage: Levels of Usage Available ~~~~~~~~~~~~~~~~~~~~~~~~~ PGAPack provides multiple levels of control to support the requirements of different users. At the simplest level, the genetic algorithm “machinery” is encapsulated within the :c:func:`PGARun` function, and the user need specify only three parameters: the data type, the string length, and the direction of optimization. All other parameters have default values. At the next level, the user calls the data-structure-neutral functions explicitly (e.g., :c:func:`PGASelect`, :c:func:`PGACrossover`, :c:func:`PGAMutate`). This mode is useful when the user wishes more explicit control over the steps of the genetic algorithm or wishes to hybridize the genetic algorithm with a hill-climbing heuristic. At the third level, the user can customize the genetic algorithm by supplying his or her own function(s) to provide a particular operator(s) while still using one of the native data types. Finally, the user can define his or her own datatype, write the data-structure-specific low-level GA functions for the datatype (i.e., crossover, mutation, etc.), and have the data-structure-specific functions executed by the high-level data-structure-neutral PGAPack functions. .. _sec:function: Function Call-Based Library ~~~~~~~~~~~~~~~~~~~~~~~~~~~ All the access to, and functionality of, the PGAPack library is provided through function calls. - The :ref:`PGASet ` family of functions sets parameter values, allele values, and specifies which GA operators to use. For example, ``PGASetPopSize (ctx, 500)`` sets the GA population size to 500. - The :ref:`PGAGet ` family of functions returns the values of fields in the context variable and allele values in the string. For example, ``bit = PGAGetBinaryAllele (ctx, p, pop, i)`` returns the value of the ``i``\ th bit in string ``p`` in population ``pop`` into ``bit``. - The simplest level of usage is provided by the :c:func:`PGARun` function. This function will run the genetic algorithm by using any nondefault values specified by the user and default values for everything else. - The next level of usage is provided by the data-structure-neutral functions, which the user can call to have more control over the specific steps of the genetic algorithm. Some of these functions are :c:func:`PGASelect`, :c:func:`PGACrossover`, :c:func:`PGAMutate`, :c:func:`PGAEvaluate`, and :c:func:`PGAFitness`. - The :ref:`data-structure-specific functions ` deal directly with native data types. In general, the user never calls these functions directly. - System calls in PGAPack provide :ref:`miscellaneous functionality `, including :ref:`debugging `, :ref:`random number generation `, :ref:`output control, and error reporting `. .. _sec:header: Header File and Symbolic Constants ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The PGAPack header file contains symbolic constants and type definitions for all functions and should be included in any file (or function or subroutine in Fortran) that calls a PGAPack function. For example, :c:macro:`PGA_CROSSOVER_UNIFORM` is a symbolic constant that is used as an argument to the function :c:func:`PGASetCrossoverType` to specify uniform crossover. In C the header file is ``pgapack.h``. In Fortran it is ``pgapackf.h`` .. _sec:evalfunc: Evaluation Function ~~~~~~~~~~~~~~~~~~~ PGAPack requires that the user supply a function that returns an evaluation of a string that it will map to a fitness value. This function is called whenever a string evaluation is required. The calling sequence and return value of the function must follow the format discussed in Section :ref:`sec:evaluation`. .. _sec:parallel: Parallelism ~~~~~~~~~~~ PGAPack can be run on both sequential computers (uniprocessors) and parallel computers (multiprocessors, multicomputers, and workstation networks). The parallel programming model used is message passing, in particular the single program, single data (SPMD) model. PGAPack supports sequential and parallel implementations of the global population model (see Chapter :ref:`chp:parallel`). .. _sec:implement: Implementation ~~~~~~~~~~~~~~ PGAPack is written in ANSI C. A set of interface functions allows most user-level PGAPack functions to be called from Fortran. All message-passing calls follow the Message Passing Interface (MPI) standard [MPI94]_, [GLS94]_, [MPI21]_. Nonoperative versions of the basic MPI functions used in the examples are supplied if the user does not provide an MPI implementation for their machine. These routines simply return and provide *no* parallel functionality. Their purpose is to allow the PGAPack library to be built in the absence of an MPI implementation. Most low-level internal functions in PGAPack are data-structure *specific* and use addresses and/or offsets of the population data structures. The user-level routines, however, provide the abstractions of data-structure *neutrality* and an integer indexing scheme for access to population data structures. .. _chp:functionality: Basic Usage +++++++++++ As the examples in Chapter :ref:`chp:examples` show, a PGAPack program can be written with just four function calls and a string evaluation function. This basic usage is discussed further in Section :ref:`sec:big-picture`. Sections :ref:`sec:stopping-criteria` to :ref:`sec:utility` explain options available in PGAPack. Section :ref:`sec:cla` discusses PGAPack command line arguments. .. _sec:big-picture: Required Functions ~~~~~~~~~~~~~~~~~~ Any file (or function or subroutine in C and Fortran) that uses a PGAPack function must include the PGAPack header file. In C this file is ``pgapack.h``. In Fortran this file is ``pgapackf.h``. The first PGAPack call made is *always* to :c:func:`PGACreate`. In C this call looks like .. code-block:: c PGAContext *ctx; ctx = PGACreate (&argc, argv, datatype, len, maxormin); :c:func:`PGACreate` allocates space for the context variable, ``ctx`` (Section :ref:`sec:context`), and returns its address. ``argc`` and ``argv`` are the standard list of arguments to a C program. ``datatype`` must be one of the constants defined in group :ref:`group:const-datatype` to specify strings consisting of binary-valued, integer-valued, real-valued, character-valued or user-defined strings, respectively. The parameter ``len`` is the length of the string (i.e., the number of genes), ``maxormin`` must be one in group :ref:`group:const-opt-dir` to indicate whether the user’s problem is maximization or minimization, respectively. In Fortran the call to :c:func:`PGACreate` is .. code-block:: fortran integer ctx ctx = PGACreate (datatype, len, maxormin) where ``datatype``, ``len``, and ``maxormin`` are the same as for C programs. After the :c:func:`PGACreate` call, the user may *optionally* set nondefault values. These are then followed by a call to :c:func:`PGASetUp` to initialize to default values all options, parameters, and operators not explicitly specified by the user. For example, .. code-block:: c ctx = PGACreate (&argc, argv, datatype, len, maxormin); PGASetPopSize (ctx, 500); PGASetFitnessType (ctx, PGA_FITNESS_RANKING); PGASetCrossoverType (ctx, PGA_CROSSOVER_UNIFORM); PGASetUniformCrossoverProb (ctx, 0.6); PGASetUp (ctx); will change the default values for the population size, the mapping of the user’s evaluation to a fitness value, and the crossover type. All :ref:`PGASet ` calls should be made *after* :c:func:`PGACreate` has been called, but *before* :c:func:`PGASetUp` has been called; all such calls are *optional*. Note also that *all* PGAPack functions other than :c:func:`PGACreate` take the context variable as their first argument. The :c:func:`PGARun` function executes the genetic algorithm. Its second argument is the name of a user-supplied evaluation function that returns a ``double`` (``double precision`` in Fortran) value that is the user’s evaluation of an individual string. In C the prototype for this function looks like .. code-block:: c double evaluate (PGAContext *ctx, int p, int pop, double *aux); and in Fortran .. code-block:: fortran double precision function evaluate (ctx, p, pop, aux) integer ctx, p, pop double precision aux(*) The user *must* write the evaluation function, and it *must* have the calling sequence shown above and discussed further in Section :ref:`sec:evaluation`, except that depending on the architecture and the calling convention of the compiler, the ``aux`` argument can be left out. After :c:func:`PGARun` terminates, :c:func:`PGADestroy` is called to release all memory allocated by PGAPack. [#]_ Except for writing an evaluation function (Section :ref:`sec:evaluation`) the information contained in rest of this chapter is optional---defaults will be set for all other GA parameters. We do note, however, that the defaults used are the result of informal testing and results reported in the GA literature. *They are by no means optimal*, and additional experimentation with other values may well yield better performance on any given problem. .. [#] :c:func:`PGADestroy` will also call ``MPI_finalize``, if MPI was started by :c:func:`PGACreate`. .. _sec:population-replacement: Population Replacement ~~~~~~~~~~~~~~~~~~~~~~ PGAPack supports several different population replacement schemes. Among them the two most common replacement schemes in the literature. The first, the *generational replacement* genetic algorithm (GRGA), replaces the entire population each generation and is the traditional genetic algorithm [Hol92]_. The second, the *steady-state* genetic algorithm (SSGA), typically replaces only a few strings each generation and is a more recent development [Sys89]_, [Whi89]_, [WK88]_. A third scheme, originally called *restricted tournament selection* by Harik [Har94]_, [Har95]_ and later adopted under the name of *restricted tournament replacement* by Pelikan [Pel05]_ replaces offspring candidates into the original population by selecting a number of members from the original population and selecting the member most similar to the candidate. The similarity metric is implemented by a genetic distance function see section :ref:`sec:basics`. The candidate is then compared to the most similar member and only if the new solution candidate is better than the member it replaces it. This approach is repeated for each new solution. A fourth approach used by evolutionary algorithm variants that mutate an individual into an offspring that replaces its parent only when it is better is also supported. This variant is used by the popular Differential Evolution [SP95]_, [SP97]_, [PSL05]_ algorithm. We call this replacement variant *pairwise best* in the following. Individuals with the same index in the old and the new population are compared and the one with the better fitness is used. Two algorithms are typically used for multi-objective optimization. The first one is the elitist Nondominated Sorting Genetic Algorithm (Version 2), NSGA-II [DPAM02]_ and is called NSGA-II replacement. It can be used for single-objective optimization, too, both with and without constraints. If constraints are present, by default the constraint violations are summed. An alternative is to use nondominated sorting for constraints, too. This can be switched on by setting :c:func:`PGASetSumConstraintsFlag` to :c:macro:`PGA_FALSE`. The second is the Nondominated Sorting Genetic Algorithm for many-objective optimization, NSGA-III [DJ14]_, [JD14]_. NSGA-II and NSGA-III are the only possible population replacement algorithms when using multi-objective optimization. Both, NSGA-II and NSGA-III use non-dominated sorting of the union of the old and the new population. This establishes ranks of non-domination among members of the population. The non-dominated sorting uses an :math:`O(n^2\cdot m)` algorithm in the original NSGA-II paper [DPAM02]_ (:math:`n` being the population size and :math:`m` the number of objectives) and was also implemented this way in PGAPack. This algorithm has recently been replaced with Jensen's divide-and-conquer algorithm [Jen03]_, later modified to correctly handle equal evaluations by Fortin et. al. [FGP13]_. It has later been shown that a slightly modified version of the algorithm is :math:`O\left(n\cdot \log(n)^{m-1}\right)` by Buzdalov and Shalyto [BS14]_. The authors of that paper think that the approximate runtime effort also applies to the original Jensen algorithm. By default the new Jensen algorithm -- which is strictly faster than the old NSGA-II algorithm with a population size larger than 100 -- is used. The old algorithm can still be selected by calling :c:func:`PGASetSortND` with the constant :c:macro:`PGA_NDSORT_NSQUARE`. For testing the old against the new algorithm, this function can be called with :c:macro:`PGA_NDSORT_BOTH` which runs both algorithms and compares the results. You should never need to set this unless you suspect a bug in the Jensen algorithm or want to compare runtimes. The default is :c:macro:`PGA_NDSORT_JENSEN`. After the nondominated sorting, NSGA-II applies a crowding metric. The original NSGA-II algorithm has been shown to be sub-optimal even in the case of two objectives [KD06a]_. The paper proposes to prune the most crowded solution one-by-one and update the metrics after each removal. The original NSGA-II pruning can be seen in figure :ref:`fig:kursawe-nsga`. You can compare this to the iterative method in figure :ref:`fig:kursawe-cd`. This metric is now the default for problems with two-objectives. .. figure:: ../kursawe-nsga.png :alt: Two dimensional crowding with original NSGA-II metric :width: 90% :name: fig:kursawe-nsga Two dimensional crowding with original NSGA-II metric .. figure:: ../kursawe-cd.png :alt: Two dimensional crowding with new iterative pruning metric :width: 90% :name: fig:kursawe-cd Two dimensional crowding with new iterative pruning metric For more than two objectives the previously described metric does not work very well. Two metrics based on nearest neighbors have been proposed in a second paper [KD06b]_. The authors define two metrics, one based on only the nearest two neighbors, one based on :math:`M` neighbors where :math:`M` is the number of objectives. An example of the original NSGA-II metric can be seen in figure :ref:`fig:crowding-nsga`. Compare this to the metric based on :math:`M` nearest neighbors which can be seen in figure :ref:`fig:crowding-mnn`. Note that the pictured DTLZ7 problem does have four disconnected Pareto-optimal regions [DTLZ05]_. The default for more than two objectives is the metric based on :math:`M` nearest neighbors. Note that depending on the problem, taking two or :math:`M` neighbors may give better results. .. figure:: ../crowding-nsga.png :alt: Crowding with original NSGA-II metric :width: 90% :name: fig:crowding-nsga Three dimensional Crowding with original NSGA-II metric .. figure:: ../crowding-mnn.png :alt: Crowding with new M-nearest neighbors metric :width: 90% :name: fig:crowding-mnn Three dimensional crowding with new M-nearest neighbors metric The metric to use can be set explicitly via the function :c:func:`PGASetCrowdingMethod`. The constant :c:macro:`PGA_CROWDING_NSGA_II` set the original NSGA-II crowding metric. The constant :c:macro:`PGA_CROWDING_CD_PRUNE` specifies the method in the first paper [KD06a]_ and is the default for two objectives. The constant :c:macro:`PGA_CROWDING_ENNS_2NN` specifies the method using two nearest neighbors and the constant :c:macro:`PGA_CROWDING_ENNS_MNN` specifies the method based on :math:`M` nearest neighbors. It is also the default for more than two objective functions. See :ref:`group:crowding-algorithms` for a summary of the available crowding algorithms. With NSGA-III you need to define a regular set of points or a set of directions where you want solutions to the multi-objective problem to be found, both can be combined, you can specify both, a number of reference directions and reference points. The reference points are in a hyperplane defined by the M positive axes of the objective space, the hyperplane goes through the axes intercepts at coordinate 1 for each of the axes. An example for three objectives with a partition size of 12 is shown in figure :ref:`fig:refpoints`. .. figure:: ../refpoints.png :alt: Reference points in 3 dimensions, partition-size 12 :name: fig:refpoints Reference points in 3 dimensions, partition-size 12 The discovered pareto-front is projected onto this hyperplane [BDR19]_. When specifying reference directions, these are directly defined in the objective space (without any projection). To compute a set of points we use an algorithm originally defined in a paper by I. Das and J. E. Dennis [DD98]_ with the function :c:func:`LIN_dasdennis`. This function gets the dimension (the number of objectives to optimize which is the number of auxiliary evaluations +1 minus the number of constraints) and the number of partitions. It returns the points in ``result`` and optionally takes a scale factor in the range 0..1 and a direction to shift this scaled set of points. The direction is only needed if the scale factor is less than one. The first time the function is called, the result must point at a NULL pointer. The function automatically allocates the necessary memory. It can be called multiple times to extend the points already allocated. The resulting points are then passed into the function :c:func:`PGASetReferencePoints`: .. code-block:: c int npoints = 0; void *result = NULL; double point [3] = {1, 1, 1}; PGASetNumAuxEval (ctx, 2); PGASetNumConstraint (ctx, 0); npoints = LIN_dasdennis (3, 2, &result, 0, 1, NULL); npoints = LIN_dasdennis (3, 1, &result, npoints, 0.5, point); PGASetReferencePoints (ctx, npoints, result); Note that if :c:func:`PGASetReferencePoints` is not called by the user, reference points are automatically allocated when calling :c:func:`PGASetUp`. All reference points are freed when calling :c:func:`PGADestroy`, so reference points must always be allocated using malloc (which is done when using :c:func:`LIN_dasdennis`). For defining reference directions, the function :c:func:`PGASetReferenceDirections` is used. It gets the number of directions and the vector of directions (each direction is a vector of the dimensionality of the number of objectives) and the number of partitions (for Das/Dennis points) and the scale factor of the generated points: .. code-block:: c double directions [][3] = {{1, 1, 1}, {1, 2, 3}}; PGASetReferenceDirections (ctx, 2, directions, 12, 0.05); The difference to the reference points above is that the reference directions are in the objective space and the Das/Dennis points are generated dynamically in each generation. Note that by default when no population size is specified, NSGA-III uses the number of points defined by the reference points and reference directions for the population size. The NSGA-III replacement optimizes the solutions to be near the reference points and/or reference directions. With a high number of objective functions, the N-dimensional space forming the solution space increases exponentially with the number of objective functions. This is known as the “curse of dimensionality”. With NSGA-II it is increasingly hard to find a well distributed set of solutions with more than two or three objectives. With the NSGA-III replacement it is possible to concentrate the search to a predefined number of reference points or reference directions. PGAPack supports both GRGA and SSGA and variants in between via *parameterized* population replacement. For example, the :ref:`PGASet ` calls .. code-block:: c PGASetPopSize (ctx, 200); PGASetNumReplaceValue (ctx, 10); PGASetPopReplaceType (ctx, PGA_POPREPL_BEST); specify that each generation a new population is created consisting of ten strings created via recombination, and the 190 most fit strings from the old population. The 190 strings can also be selected randomly, with or without replacement, by setting the second argument of :c:func:`PGASetPopReplaceType` to :c:macro:`PGA_POPREPL_RANDOM_REP` or :c:macro:`PGA_POPREPL_RANDOM_NOREP`, respectively. For selecting restricted tournament replacement :c:macro:`PGA_POPREPL_RTR` is used. The default for the window size (number of members of the old population that are chosen for comparison with a new candidate) is min :math:`(n, N/20)` where :math:`n` is the string length and :math:`N` is the population size [Pel05]_. The window size can be set or queried with :c:func:`PGASetRTRWindowSize` and :c:func:`PGAGetRTRWindowSize`, respectively. Note that when restricted tournament replacement is in use, the maximum number of new candidates is limited with the number set with :c:func:`PGASetNumReplaceValue` but fewer—depending on fitness–may be replaced into the new population. Note that it depends on the selection which individuals in the old population are replaced. Since restricted tournament replacement is an elitist strategy the overall fitness never dimishes with this replacement strategy. For pairwise best replacement :c:macro:`PGA_POPREPL_PAIRWISE_BEST` is used as the replacement type. Like restricted tournament replacement it is an elitist strategy. For NSGA-II replacement :c:macro:`PGA_POPREPL_NSGA_II` is used. For NSGA-III replacement :c:macro:`PGA_POPREPL_NSGA_III` is used. The number of auxiliary evaluation function can be set with :c:func:`PGASetNumAuxEval` and the number of constraints can be set with :c:func:`PGASetNumConstraint`. If the difference between the two is :math:`>0` (i.e. the number of objectives is :math:`>1`), these auxiliary evaluations are used for multi-objective optimization. Only the NSGA-II and NSGA-III replacement are possible with these settings (i.e. when the number of objectives is :math:`>1`). The replacement types *pairwise best*, *restricted tournament replacement*, NSGA-II, and NSGA-III replacement have selection pressure in addition to providing a population replacement strategy. So these can be used if a selection scheme without selection pressure (a tournament strategy with only one participant in the tournament or linear selection) is used. By default, the number of new strings created each generation is 10 percent of the population size (an SSGA population replacement strategy). A GRGA can be implemented by setting :c:func:`PGASetNumReplaceValue` to the population size (the default population size is 100). Setting :c:func:`PGASetNumReplaceValue` to one less than the population size will result in an elitist GRGA, where the most fit string is always copied to the new population (since :c:macro:`PGA_POPREPL_BEST` is the default population replacement strategy). Traditionally, strings created through recombination first undergo crossover and then mutation. Some practitioners [Dav91]_ have argued that these two operators should be separate. By default, PGAPack applies mutation only to strings that did *not* undergo crossover. This is equivalent to setting :c:func:`PGASetMixingType` to :c:macro:`PGA_MIX_MUTATE_OR_CROSS` which is also the default. To have strings undergo *both* crossover and mutation, one should set :c:func:`PGASetMixingType` to :c:macro:`PGA_MIX_TRADITIONAL`. Note that there is also a mode that will not mutate strings that are not also crossed over. This can be enabled by setting :c:func:`PGASetMixingType` to :c:macro:`PGA_MIX_MUTATE_AND_CROSS`. If an evolutionary algorithm variant without crossover is used or if special crossover techniques with more that two parents should be applied, all the logic can be implemented in a custom crossover operator and the :c:func:`PGASetMixingType` can be set to :c:macro:`PGA_MIX_MUTATE_ONLY`. In this mode no crossover is performed at all. There is also a legacy interface which should *not* be used for new code. Functions used in that interface are: :c:func:`PGASetMutationOrCrossoverFlag`, :c:func:`PGASetMutationAndCrossoverFlag`, and :c:func:`PGASetMutationOnlyFlag`. By default, PGAPack allows duplicate strings in the population. Some practitioners advocate not allowing duplicate strings in the population in order to maintain diversity. The function call :c:func:`PGASetNoDuplicatesFlag` with the parameters ``(ctx, PGA_TRUE)`` will not allow duplicate strings in the population: It repeatedly applies the mutation operator (with an increasing mutation rate) to a duplicate string until it no longer matches any string in the new population. If the mutation rate exceeds 1.0, however, the duplicate string *will* be allowed in the population, and a warning message will be issued. Figure :ref:`fig:popreplace` shows the generic population replacement scheme in PGAPack. Both populations :math:`k` and :math:`k+1` are of fixed size (the value returned by :c:func:`PGAGetPopSize`). First, :c:func:`PGAGetPopSize` - :c:func:`PGAGetNumReplaceValue` strings are copied over directly from generation :math:`k`. The way the strings are chosen, the most fit, or randomly with or without replacement, depends on the value set by :c:func:`PGASetPopReplaceType`. The remaining :c:func:`PGAGetNumReplaceValue` strings are created by crossover and mutation. .. figure:: ../generation.svg :alt: Population Replacement :name: fig:popreplace Population Replacement .. _sec:stopping-criteria: Stopping Criteria ~~~~~~~~~~~~~~~~~ PGAPack terminates when at least one of the stopping rule(s) specified has been met. The three stopping rules are (1) iteration limit exceeded, (2) population too similar, and (3) no change in the best solution found in a given number of iterations. The default is to stop when the iteration limit (by default, 1000 iterations) is reached. Note that when :math:`\epsilon`-constraint optimization is in use, stopping is not triggered as long as :math:`\epsilon>0`, see section :ref:`sec:evaluation`. The choice of stopping rule is set by :c:func:`PGASetStoppingRuleType`. For example, :c:func:`PGASetStoppingRuleType` with parameters ``(ctx, PGA_STOP_MAXITER)`` is the default. Other choices are :c:macro:`PGA_STOP_TOOSIMILAR` and :c:macro:`PGA_STOP_NOCHANGE` for population too similar and no change in the best solution found, respectively. :c:func:`PGASetStoppingRuleType` may be called more than once. The different stopping rules specified are *or*\ ed together. If :c:macro:`PGA_STOP_MAXITER` is one of the stopping rules, :c:func:`PGASetMaxGAIterValue` with parameters ``(ctx, 500)`` will change the maximum iteration limit to 500. If :c:macro:`PGA_STOP_NOCHANGE` is one of the stopping rules, :c:func:`PGASetMaxNoChangeValue` with parameters ``(ctx, 50)`` will change from 100 (the default) to 50 the maximum number of iterations in which no change in the best evaluation is allowed before the GA stops. If :c:macro:`PGA_STOP_TOOSIMILAR` is one of the stopping rules, :c:func:`PGASetMaxSimilarityValue` with parameters ``(ctx, 99)`` will change from 95 to 99 the percentage of the population allowed to have the same evaluation function value before the GA stops. .. _sec:initialization: Initialization ~~~~~~~~~~~~~~ Strings are either initialized randomly (the default), or set to zero. The choice is specified by setting the second argument of :c:func:`PGASetRandomInitFlag` to either :c:macro:`PGA_TRUE` or :c:macro:`PGA_FALSE`, respectively. Random initialization depends on the datatype. If binary-valued strings are used, each gene is set to ``1`` or ``0`` with an equal probability. To set the probability of randomly setting a bit to ``1`` to 0.3, use :c:func:`PGASetBinaryInitProb` with parameters ``(ctx, 0.3)``. For integer-valued strings, the default is to set the strings to a permutation on a range of integers. The default range is :math:`[0,L-1]`, where :math:`L` is the string length. :c:func:`PGASetIntegerInitPermute` with parameters ``(ctx, 500, 599)`` will set the permutation range to :math:`[500,599]`. The length of the range *must* be the same as the string length. Alternatively, :c:func:`PGASetIntegerInitRange` will set each gene to a random value selected uniformly from a specified range. For example, the code .. code-block:: c stringlen = PGAGetStringLength (ctx); for (i=0;i double evaluate (PGAContext *ctx, int p, int pop, double *aux); int myMutation (PGAContext *ctx, int p, int pop, double pm); int main (int argc, char **argv) { PGAContext *ctx; int i, maxiter; ctx = PGACreate (&argc, argv, PGA_DATATYPE_INTEGER, 10, PGA_MAXIMIZE); PGASetUserFunction (ctx, PGA_USERFUNCTION_MUTATION, myMutation); PGASetIntegerInitPermute (ctx, 1, 10); PGASetUp (ctx); PGARun (ctx, evaluate); PGADestroy (ctx); return 0; } int myMutation (PGAContext *ctx, int p, int pop, double pm) { int stringlen, i, k, count = 0; stringlen = PGAGetStringLength (ctx); for (i = 0; i < stringlen; i++) if (PGARandomFlip (ctx, pm)) { k = PGARandomInterval (ctx, 1, stringlen); PGASetIntegerAllele (ctx, p, pop, i, k); count++; } return (double) count; } double evaluate (PGAContext *ctx, int p, int pop, double *aux) { int stringlen, i, sum = 0; stringlen = PGAGetStringLength (ctx); for (i = 0; i < stringlen; i++) sum += PGAGetIntegerAllele (ctx, p, pop, i); return (double)sum; } Example Problem: Fortran ~~~~~~~~~~~~~~~~~~~~~~~~ Figure :ref:`example:maxbit-custom-f77` is the same example as in Figure :ref:`example:maxbit-custom` written in Fortran. .. _example:maxbit-custom-f77: .. code-block:: fortran :caption: PGAPack Fortran Example Using Custom Mutation Operator include 'pgapackf.h' include 'mpif.h' double precision evaluate integer myMutation external evaluate, myMutation integer ctx, i, maxiter, ierror call MPI_Init (ierror) ctx = PGACreate (PGA_DATATYPE_INTEGER, 10, PGA_MAXIMIZE) call PGASetUserFunction (ctx, PGA_USERFUNCTION_MUTATION, & myMutation) call PGASetIntegerInitPermute(ctx, 1, 10); call PGASetUp (ctx); call PGARun (ctx, evaluate); call PGADestroy (ctx); call MPI_Finalize(ierror) stop end integer function myMutation(ctx, p, pop, pm) include 'pgapackf.h' integer ctx, p, pop double precision pm integer stringlen, i, k, count count = 0 stringlen = PGAGetStringLength(ctx) do i=0, stringlen if (PGARandomFlip(ctx, pm) .eq. PGA_TRUE) then k = PGARandomInterval(ctx, 1, stringlen) call PGASetIntegerAllele(ctx, p, pop, i, k) count = count + 1 endif enddo myMutation = count return end double precision function evaluate(ctx, p, pop) include 'pgapackf.h' integer ctx, p, pop integer stringlen, i, sum sum = 0 stringlen = PGAGetStringLength(ctx) do i=0, stringlen sum = sum + PGAGetIntegerAllele(ctx, p, pop, i) enddo evaluate = sum return end .. _chp:new-data: Custom Usage: New Data Types ++++++++++++++++++++++++++++ This chapter discusses how PGAPack may be extended by defining a new data type. Defining a new data type may be done only in C programs. .. _basics-1: Basics ~~~~~~ .. _tab:new-functions: .. table:: Functions Required for New Data Types ================== ========================================== Memory allocation :c:macro:`PGA_USERFUNCTION_CREATESTRING` Memory free :c:macro:`PGA_USERFUNCTION_CHROM_FREE` String packing :c:macro:`PGA_USERFUNCTION_BUILDDATATYPE` Mutation :c:macro:`PGA_USERFUNCTION_MUTATION` Crossover :c:macro:`PGA_USERFUNCTION_CROSSOVER` String printing :c:macro:`PGA_USERFUNCTION_PRINTSTRING` String copying :c:macro:`PGA_USERFUNCTION_COPYSTRING` Duplicate checking :c:macro:`PGA_USERFUNCTION_DUPLICATE` Hashing :c:macro:`PGA_USERFUNCTION_HASH` ================== ========================================== .. _tab:serialization-functions: .. table:: Serialization API ================== =========================================== Serialization :c:macro:`PGA_USERFUNCTION_SERIALIZE` Free serialization :c:macro:`PGA_USERFUNCTION_SERIALIZE_FREE` Deserialization :c:macro:`PGA_USERFUNCTION_DESERIALIZE` ================== =========================================== To create a new data type, you must (1) specify :c:macro:`PGA_DATATYPE_USER` for the datatype in the :c:func:`PGACreate` call and (2) for *each* entry in Table :ref:`tab:new-functions`, call :c:func:`PGASetUserFunction` to specify the function that will perform the given operation on the new data type. If the data type is :c:macro:`PGA_DATATYPE_USER`, the string length specified to :c:func:`PGACreate` can be whatever the user desires. It will be returned by :c:func:`PGAGetStringLength` but is not otherwise used in the data-structure-neutral functions of PGAPack. When specifying a user function for string creation with :c:macro:`PGA_USERFUNCTION_CREATESTRING`, by default the string is freed using the ``free`` function. If memory allocation uses different mechanisms, a user function for freeing a chromosome can be specified with :c:macro:`PGA_USERFUNCTION_CHROM_FREE`. Instead of specifying a user function for building an ``MPI`` data type, you can *instead* specify user functions for a serialization API summarized in table :ref:`tab:serialization-functions`. The user function for serialization :c:macro:`PGA_USERFUNCTION_SERIALIZE` is used on the sending side. The function for deserialization :c:macro:`PGA_USERFUNCTION_DESERIALIZE` is used at the receiving side. With the serialization API it is possible to send/receive variable-length data types. The serialization must reserve memory for the serialized representation. If it uses memory allocation with ``malloc``, the default is to call ``free`` when the serialized value is no longer needed. If a memory allocation system not compatible with ``free`` is used, the user function :c:macro:`PGA_USERFUNCTION_SERIALIZE_FREE` must be defined. When using the serialization API a user function :c:macro:`PGA_USERFUNCTION_BUILDDATATYPE` *must not* be defined. The calling sequences for the functions in Table :ref:`tab:new-functions` are given in Table :ref:`tab:new-functions1`. Template routines for these functions are in the file ``./examples/templates/uf_new.c``. The functions :c:macro:`PGA_USERFUNCTION_DUPLICATE` and :c:macro:`PGA_USERFUNCTION_HASH` for user defined data types are needed only when duplicate checking is enabled with :c:func:`PGASetNoDuplicatesFlag`. Note that for duplicate checking to work, usually *both*, the hashing and the duplicate function need to be defined. An example is given in ``examples/c/namefull.c`` for C and ``examples/fortran/namefull.f`` for Fortran. These define a hash function and a duplicate check (comparison) function that treat all non-matching characters alike. When implementing a hash function for user defined datatypes the function :c:func:`PGAUtilHash` in ``source/utility.c`` can be used. .. _tab:new-functions1: .. table:: Calling Sequences for New Data Type Functions +--------------------------------------------+------------------+-----------------------------------------------------------------+ | :c:macro:`PGA_USERFUNCTION_CREATESTRING` | ``void`` | ``(PGAContext*, int p, int pop, int flag)`` | +--------------------------------------------+------------------+-----------------------------------------------------------------+ | :c:macro:`PGA_USERFUNCTION_CHROM_FREE` | ``void`` | ``(PGAContext*)`` | +--------------------------------------------+------------------+-----------------------------------------------------------------+ | :c:macro:`PGA_USERFUNCTION_BUILDDATATYPE` | ``MPI_Datatype`` | ``(PGAContext*, int p, int pop)`` | +--------------------------------------------+------------------+-----------------------------------------------------------------+ | :c:macro:`PGA_USERFUNCTION_MUTATION` | ``int`` | ``(PGAContext*, int p, int pop, double prob)`` | +--------------------------------------------+------------------+-----------------------------------------------------------------+ | :c:macro:`PGA_USERFUNCTION_CROSSOVER` | ``void`` | ``(PGAContext*, int, int, int, int, int, int)`` | +--------------------------------------------+------------------+-----------------------------------------------------------------+ | :c:macro:`PGA_USERFUNCTION_PRINTSTRING` | ``void`` | ``(PGAContext*, FILE *, int p, int pop)`` | +--------------------------------------------+------------------+-----------------------------------------------------------------+ | :c:macro:`PGA_USERFUNCTION_COPYSTRING` | ``int`` | ``(PGAContext*, int p1, int pop1, int p2, int pop2)`` | +--------------------------------------------+------------------+-----------------------------------------------------------------+ | :c:macro:`PGA_USERFUNCTION_DUPLICATE` | ``int`` | ``(PGAContext*, int p1, int pop1, int p2, int pop2)`` | +--------------------------------------------+------------------+-----------------------------------------------------------------+ | :c:macro:`PGA_USERFUNCTION_SERIALIZE` | ``size_t`` | ``(PGAContext*, int p, int pop, const void **ser)`` | +--------------------------------------------+------------------+-----------------------------------------------------------------+ | :c:macro:`PGA_USERFUNCTION_DESERIALIZE` | ``void`` | ``(PGAContext*, int p, int pop, const void *ser, size_t size)`` | +--------------------------------------------+------------------+-----------------------------------------------------------------+ | :c:macro:`PGA_USERFUNCTION_SERIALIZE_FREE` | ``void`` | ``(void *)freeme`` | +--------------------------------------------+------------------+-----------------------------------------------------------------+ While PGAPack requires that the user supply all the functions in Table :ref:`tab:new-functions`, your program may not require the functionality of all of them. For example, the user really does not need to write a function to pack the strings for message-passing unless a parallel version of PGAPack is being used. In these cases, we suggest that the user supply a stub function; i.e., a function with the correct calling sequence but no functionality. Example Problem ~~~~~~~~~~~~~~~ .. _example1:new-datatype-main: .. code-block:: c :caption: Main Program for Structure Data Type #include double energy (double *, int *); double Evaluate (PGAContext *, int, int); void CreateString (PGAContext *, int, int, int); int Mutation (PGAContext *, int, int, double); void Crossover (PGAContext *, int, int, int, int, int, int); void WriteString (PGAContext *, FILE *, int, int); void CopyString (PGAContext *, int, int, int, int); int DuplicateString (PGAContext *, int, int, int, int); MPI_Datatype BuildDT (PGAContext *, int, int); typedef struct { double t[6]; /* ligand translation and rotation */ int sc[40]; /* ligand sidechain rotations */ } ligand; int main (int argc, char **argv) { PGAContext *ctx; int maxiter; ctx = PGACreate (&argc, argv, PGA_DATATYPE_USER, 46, PGA_MINIMIZE); PGASetRandomSeed (ctx, 1); PGASetMaxGAIterValue (ctx, 5000); PGASetUserFunction (ctx, PGA_USERFUNCTION_CREATESTRING, CreateString); PGASetUserFunction (ctx, PGA_USERFUNCTION_MUTATION, Mutation); PGASetUserFunction (ctx, PGA_USERFUNCTION_CROSSOVER, Crossover); PGASetUserFunction (ctx, PGA_USERFUNCTION_PRINTSTRING, WriteString); PGASetUserFunction (ctx, PGA_USERFUNCTION_COPYSTRING, CopyString); PGASetUserFunction (ctx, PGA_USERFUNCTION_DUPLICATE, DuplicateString); PGASetUserFunction (ctx, PGA_USERFUNCTION_BUILDDATATYPE, BuildDT); PGASetUp (ctx); PGARun (ctx, Evaluate); PGADestroy (ctx); return 0; } This example illustrates use of a user-defined structure as the new data type. The problem is one of molecular docking where one protein molecule (the ligand) is to be docked into a second, target protein molecule. Figure :ref:`example1:new-datatype-main` contains the function prototypes for each function that will operate on the new datatype, the definition of the user’s structure ``ligand``, and the main program. The first three ``doubles`` of the array ``t`` in structure ``ligand`` represent the translation of the ligand molecule in the :math:`x`-, :math:`y`-, and :math:`z`-axes, respectively. The last three ``doubles`` in the array ``t`` represent the rotation of the ligand molecule about each of the axes. The ``int``\ s in the ``sc`` array represent side chain rotations (which are discrete) of the ligand molecule. .. _example1:new-datatype-create: .. code-block:: c :caption: Creation and Initialization Function for Structure Data Type void CreateString (PGAContext *ctx, int p, int pop, int InitFlag) { int i; ligand *ligand_ptr; PGAIndividual *new; new = PGAGetIndividual (ctx, p, pop); if (!(new->chrom = malloc (sizeof (ligand)))) { fprintf (stderr, "No room for new->chrom"); exit (1); } ligand_ptr = (ligand *)new->chrom; if (InitFlag) { for (i = 0; i < 3; i++) ligand_ptr->t[i] = PGARandom01 (ctx, 0) * 20.0 - 10.0; for (i = 3; i < 6; i++) ligand_ptr->t[i] = PGARandom01 (ctx, 0) * 6.28 - 3.14; for (i = 0; i < 40; i++) ligand_ptr->sc[i] = PGARandomInterval (ctx, -20, 20); } else { for (i = 0; i < 6; i++) ligand_ptr->t[i] = 0.0; for (i = 0; i < 40; i++) ligand_ptr->sc[i] = 0; } } Figure :ref:`example1:new-datatype-create` contains the function ``CreateString`` that allocates and initializes the ligand structure. At this level of usage it is no longer always possible to maintain the ``(p, pop)`` abstraction to specify an individual (a string and associated fields). ``CreateString`` works directly with the string pointer that ``(p, pop)`` is mapped to. If ``InitFlag`` is true, ``CreateString`` will initialize the fields; otherwise they are set to 0. :c:func:`PGAGetIndividual` with parameters ``(ctx, p, pop)`` returns a pointer of type :c:type:`PGAIndividual` to the individual (the string and associated fields) specified by ``(p, pop)``. :c:type:`PGAIndividual` is a structure, one of the fields of which is ``chrom``, a ``void`` pointer to the string itself. That pointer, ``new->chrom``, is assigned the address of the memory allocated by the ``malloc`` function. As ``malloc`` returns a ``void`` pointer, no cast is necessary. The value of ``InitFlag`` is passed by PGAPack to the user’s string creation routine. It specifies whether to randomly initialize the string or set it to zero. By default, :c:macro:`PGA_OLDPOP` (except for :c:macro:`PGA_TEMP1` and :c:macro:`PGA_TEMP1` which are set to zero) is randomly initialized, and :c:macro:`PGA_NEWPOP` is set to zero. This choice may be changed with the :c:func:`PGASetRandomInitFlag` function discussed in Section :ref:`sec:initialization`.) .. _example1:new-datatype-mutation: .. code-block:: c :caption: Mutation for Structure Data Type int Mutation (PGAContext *ctx, int p, int pop, double mr) { ligand *ligand_ptr; int i, count = 0; ligand_ptr = (ligand *)PGAGetIndividual (ctx, p, pop)->chrom; for (i = 0; i < 6; i++) if (PGARandomFlip (ctx, mr)) { if (PGARandomFlip (ctx, 0.5)) ligand_ptr->t[i] += 0.1*ligand_ptr->t[i]; else ligand_ptr->t[i] -= 0.1*ligand_ptr->t[i]; count++; } for (i = 0; i < 40; i++) if (PGARandomFlip (ctx, mr)) { if (PGARandomFlip (ctx, 0.5)) ligand_ptr->sc[i] += 1; else ligand_ptr->sc[i] -= 1; count++; } return (count); } Figure :ref:`example1:new-datatype-mutation` contains the mutation function ``Mutation`` for the ligand data type. Each of the 46 genes has a probability of ``mr`` of being changed. If a mutation occurs, ``Mutation`` adds or subtracts one tenth to the existing value of a ``double``, and adds or subtracts one to an ``int``. .. _example1:new-datatype-crossover: .. code-block:: c :caption: Crossover for Structure Data Type void Crossover (PGAContext *ctx, int p1, int p2, int pop1, int t1, int t2, int pop2) { int i; ligand *parent1, *parent2, *child1, *child2; double pu; parent1 = (ligand *)PGAGetIndividual (ctx, p1, pop1)->chrom; parent2 = (ligand *)PGAGetIndividual (ctx, p2, pop1)->chrom; child1 = (ligand *)PGAGetIndividual (ctx, t1, pop2)->chrom; child2 = (ligand *)PGAGetIndividual (ctx, t2, pop2)->chrom; pu = PGAGetUniformCrossoverProb (ctx); for (i = 0; i < 6; i++) if (PGARandomFlip (ctx, pu)) { child1->t[i] = parent1->t[i]; child2->t[i] = parent2->t[i]; } else { child1->t[i] = parent2->t[i]; child2->t[i] = parent1->t[i]; } for (i = 0; i < 40; i++) if (PGARandomFlip (ctx, pu)) { child1->sc[i] = parent1->sc[i]; child2->sc[i] = parent2->sc[i]; } else { child1->sc[i] = parent2->sc[i]; child2->sc[i] = parent1->sc[i]; } } Figure :ref:`example1:new-datatype-crossover` contains the crossover function ``Crossover``, which implements uniform crossover for the ligand data type. The lines .. code-block:: c parent1 = (ligand *)PGAGetIndividual (ctx, p1, pop1)->chrom; parent2 = (ligand *)PGAGetIndividual (ctx, p2, pop1)->chrom; child1 = (ligand *)PGAGetIndividual (ctx, t1, pop2)->chrom; child2 = (ligand *)PGAGetIndividual (ctx, t2, pop2)->chrom; are worthy of mention. Each implements in one line what the two lines .. code-block:: c new = PGAGetIndividual (ctx, p, pop); string = (ligand *)new->chrom; in :ref:`example1:new-datatype-mutation` did. Either style is acceptable. :c:func:`PGAGetIndividual` returns a pointer whose ``chrom`` field (a ``void`` pointer) is cast to the ``ligand`` data type. .. _example1:new-datatype-duplicate: .. code-block:: c :caption: Duplicate Testing for Structure Data Type int DuplicateString (PGAContext *ctx, int p1, int pop1, int p2, int pop2) { void *a, *b; a = PGAGetIndividual (ctx, p1, pop1)->chrom; b = PGAGetIndividual (ctx, p2, pop2)->chrom; return (!memcmp (a, b, sizeof (ligand))); } .. _example1:new-datatype-hash: .. code-block:: c :caption: Hashing for Structure Data Type PGAHash HashString (PGAContext *ctx, int p, int pop) { void *lig = PGAGetIndividual (ctx, p, pop)->chrom; return PGAUtilHash (lig, sizeof (ligand), PGA_INITIAL_HASH); } Figure :ref:`example1:new-datatype-duplicate` contains the code for ``DuplicateString``, which checks for duplicate ligand structures. It uses the ANSI C ``memcmp`` function for this purpose. In figure :ref:`example1:new-datatype-hash` the code for the hashing function is shown. It uses the utility function :c:func:`PGAUtilHash` for computing the hash over the user-defined data structure. .. _example1:new-datatype-evaluate: .. code-block:: c :caption: Evaluation Function for Structure Data Type double Evaluate (PGAContext *ctx, int p, int pop, double *aux) { int i, j; double x[6]; int sc[40]; PGAIndividual *ind; ligand *lig; lig = (ligand *)PGAGetIndividual (ctx, p, pop)->chrom; for (i = 0; i < 6; i++) x[i] = lig->t[i]; for (i = 0; i < 40; i++) sc[i] = lig->sc[i]; return energy (x, sc); } Figure :ref:`example1:new-datatype-evaluate` contains the evaluation function for this example. It again uses :c:func:`PGAGetIndividual` to map ``(p, pop)`` into a pointer to the string of interest. For user data types, PGAPack does *not* provide a ``PGAGetUserAllele`` function, so access to the allele values is made directly through the pointer. .. _example1:new-datatype-build: .. code-block:: c :caption: Message Packing Function for Structure Data Type MPI_Datatype BuildDT (PGAContext *ctx, int p, int pop) { int idx = 0; int counts [PGA_MPI_HEADER_ELEMENTS + 2]; MPI_Aint displs [PGA_MPI_HEADER_ELEMENTS + 2]; MPI_Datatype types [PGA_MPI_HEADER_ELEMENTS + 2]; MPI_Datatype DT_PGAIndividual; PGAIndividual *P; ligand *S; P = PGAGetIndividual (ctx, p, pop); S = (ligand *)P->chrom; idx = PGABuildDatatypeHeader (ctx, p, pop, counts, displs, types); /* Finish the MPI datatype. Every user defined function needs these. * The stuff internal to PGAPack is already handled by * PGABuildDatatypeHeader above. */ MPI_Get_address (S->t, &displs [idx]); counts [idx] = 6; types [idx] = MPI_DOUBLE; idx++; MPI_Get_address (S->sc, &displs [idx]); counts [idx] = 40; types [idx] = MPI_INT; idx++; MPI_Type_struct (idx, counts, displs, types, &DT_PGAIndividual); MPI_Type_commit (&DT_PGAIndividual); return DT_PGAIndividual; } Figure :ref:`example1:new-datatype-build` contains the function ``BuildDT`` that builds an MPI datatype for sending strings to other processors. Consult an MPI manual for further information. .. _chp:hill-climbing: Hill-Climbing and Hybridization +++++++++++++++++++++++++++++++ Hill-climbing heuristics attempt to improve a solution by moving to a better *neighbor* solution. Whenever the neighboring solution is better than the current solution, it replaces the current solution. Genetic algorithms and hill-climbing heuristics have complementary strong and weak points. GAs are good at finding promising areas of the search space, but not as good at fine-tuning within those areas. Hill-climbing heuristics, on the other hand, are good at fine-tuning, but lack a global perspective. Practice has shown that a *hybrid* algorithm that combines GAs with hill-climbing heuristics often results in an algorithm that can outperform either one individually. There are two general schemes for creating hybrid algorithms. The simplest is to run the genetic algorithm until it terminates and then apply a hill-climbing heuristic to each (or just the best) string. The second approach is to integrate a hill-climbing heuristic with the genetic algorithm. Choices to be made in the second case include how often to apply the hill-climbing heuristic and how many strings in the population to apply it to. PGAPack supports hybrid schemes in the following ways: - By passing the context variable as a parameter to the user's hill-climbing function, the user has access to solution and parameter values, debug flags, and other information. - The functions :c:func:`PGAGetBinaryAllele`, :c:func:`PGAGetIntegerAllele`, :c:func:`PGAGetRealAllele`, and :c:func:`PGAGetCharacterAllele` allow the user’s hill-climbing function to read allele values, and the functions :c:func:`PGASetBinaryAllele`, :c:func:`PGASetIntegerAllele`, :c:func:`PGASetRealAllele`, and :c:func:`PGASetCharacterAllele` allow the user’s hill-climbing function to set allele values explicitly. - The functions :c:func:`PGAGetRealFromBinary`, :c:func:`PGAGetRealFromGrayCode`, :c:func:`PGAGetIntegerFromBinary`, and :c:func:`PGAGetIntegerFromGrayCode` allow the user’s hill-climbing function to read integer or real numbers encoded as binary or Gray code strings. - The functions :c:func:`PGAEncodeRealAsBinary`, :c:func:`PGAEncodeRealAsGrayCode`, :c:func:`PGAEncodeIntegerAsBinary`, and :c:func:`PGAEncodeIntegerAsGrayCode` allow the user’s hill-climbing function to encode integer or real numbers as binary or Gray code strings. - The functions :c:func:`_PGAGetEvaluation` and :c:func:`_PGASetEvaluation` allow the user’s hill-climbing function to get and set evaluation function values, and :c:func:`PGASetEvaluationUpToDateFlag` and :c:func:`PGAGetEvaluationUpToDateFlag` to get and set the flag that indicates whether an evaluation function value is up to date. These functions have an optional fifth argument ``aux``, that allows to get or set the auxiliary evaluations, see section :ref:`sec:evaluation`. The preferred way to run a hybrid GA and use :c:func:`PGARun` is to use the :c:func:`PGASetUserFunction` discussed in Chapter :ref:`chp:custom1` to specify a user function to be called before evaluation of a new individual using :c:macro:`PGA_USERFUNCTION_HILLCLIMB`. An example can be found in ``./examples/c/maxbit-hc.c``. When using a parallel version this is called in the parallel processes and so the hillclimbing is also parallelized. The hillclimbing function can chose to evaluate the given individual. Then it has to set the evaluation using :c:func:`PGASetEvaluation`, this will also set the up-to-date flag, so an extra call to :c:func:`PGASetEvaluationUpToDateFlag` is not needed. When the up-to-date flag is set, this signals to PGAPack that the evaluation is already done. If no evaluation is performed, the evaluation function is called by PGAPack after the hillclimber. A more flexible approach would be for the user to call the high-level PGAPack functions, and their hillclimber to explicitly specify the steps of the hybrid GA. Figure :ref:`example:hill` is a version of the Maxbit problem given in Section :ref:`sec:simple-example`. It uses the hill-climbing function ``hillclimb``, which is called after the recombination step. It randomly selects a gene to set to one. Note the :c:func:`PGASetEvaluationUpToDateFlag` call. It sets the flag that indicates the evaluation function is not current with the string (since the string was changed). It is *critical* that this flag be set when the user changes a string, since the value of this flag determines whether :c:func:`PGAEvaluate` will invoke the user’s function evaluation routine. .. _example:hill: .. code-block:: c :caption: Hill-Climbing Heuristic for Maxbit Example #include "pgapack.h" double evaluate (PGAContext *, int, int, double *aux); void hillclimb (PGAContext *, int); int main (int argc, char **argv) { PGAContext *ctx; ctx = PGACreate (&argc, argv, PGA_DATATYPE_BINARY, 100, PGA_MAXIMIZE); PGASetUp (ctx); PGAEvaluate (ctx, PGA_OLDPOP, evaluate, NULL); PGAFitness (ctx, PGA_OLDPOP); while (!PGADone (ctx, NULL)) { PGASelect (ctx, PGA_OLDPOP); PGARunMutationAndCrossover (ctx, PGA_OLDPOP, PGA_NEWPOP); hillclimb (ctx, PGA_NEWPOP); PGAEvaluate (ctx, PGA_NEWPOP, evaluate, NULL); PGAFitness (ctx, PGA_NEWPOP); PGAUpdateGeneration (ctx, NULL); PGAPrintReport (ctx, stdout, PGA_OLDPOP); } PGADestroy (ctx); return 0; } void hillclimb (PGAContext *ctx, int pop) { int i, p, popsize, stringlen; popsize = PGAGetPopSize (ctx); stringlen = PGAGetStringLength (ctx); for (p=0; p`` or ``-pgadebug ``. Several forms of the ```` argument are allowed. ``-pgadbg 12`` will trace entering all high-level functions as shown in Figure :ref:`fig:trace`. ``-pgadbg 12,13`` or ``-pgadbg 12-13`` will trace entering *and* exiting of all high-level functions. The command line option ``-pgahelp debug`` will list the debug level options and then exit. Fortran (and C) programmers may access the trace facility via function calls. The function :c:func:`PGASetDebugLevel` may be called to set a debug level. For example, .. code-block:: fortran call PGASetDebugLevel(ctx, 12) would produce the same output shown in Figure :ref:`fig:trace`. :c:func:`PGAClearDebugLevel` with parameter 12 will clear prints associated with debug level 12. :c:func:`PGAPrintDebugOptions` will print the list of available debug options. The function :c:func:`PGASetDebugLevelByName` will turn on debugging of the named function. For example, ``PGASetDebugLevelByName(ctx, "PGACrossover")`` will enable all the trace prints of :c:func:`PGACrossover`. :c:func:`PGAClearDebugLevelByName` will disable the tracing of the specified function. Users can use the trace facility in their own functions (e.g., their evaluation function) in two ways. First, they can insert :c:func:`PGADebugPrint` function calls in their functions using one of the symbolic constants defined in the header file ``pgapack.h``. These are documented in group :ref:`group:const-debug`. Events for which debug output can be printed include entering a function, exiting a function, allocating memory, print a variable’s value, and sending or receiving a string, respectively. For example, .. code-block:: c PGADebugPrint (ctx, PGA_DEBUG_ENTERED, "MyFunc", "Entered", PGA_VOID, NULL) will print the line:: 0: MyFunc : Entered when the debug level of 12 is specified. .. code-block:: c PGADebugPrint (ctx, PGA_DEBUG_PRINTVAR, "MyFunc", "i = ", PGA_INT, (void *)&i) will print the line:: 0: MyFunc : i = 1 when the debug level of 82 is specified. Users can also use the reserved debug levels of 1–10 to customize the trace facilities for use in their own functions. For example .. code-block:: c PGADebugPrint (ctx, 5, "MyFunc", "After call to MyCleanUp", PGA_VOID, NULL); will print the line:: 0: MyFunc : After call to MyCleanUp when the debug level of five is specified. Note that we use ``MPI_COMM_WORLD`` (1) for the random number seed and (2) for :c:func:`PGADebugPrint` calls.