The GA Procedure

Defining User Genetic Operators

You can define new genetic operators with subroutines. The GA procedure calls your subroutine when it is necessary to perform mutation or crossover operations. You can designate that a subroutine be used for crossover with the call

call SetCrossRoutine( 'routine');

where routine is the name of the subroutine you have defined. Similarly, you can designate a subroutine for the mutation operator with the call

call SetMutRoutine( 'routine');

The subroutine name must be a quoted string. The first parameter of the crossover or mutation subroutine you define must be a numeric array. When the GA procedure calls your subroutine, it passes information in the first parameter, referred to as the selection parameter, which designates the selected members for the operation. You should not alter the selection parameter in any way, but pass it unchanged into special utility routines provided by the GA procedure in order to obtain the solution elements and write them to the selected members. You can define as many other parameters to your subroutine as you need; they are filled in with values from variables of the same name created in the global portion of your program. Any array parameters must be numeric and of the type /NOSYMBOLS.

For a crossover subroutine, use the ReadParent call to get the elements of the selected parents into arrays that you can then manipulate with programming statements. The results can be written to the designated offspring with a WriteChild call. The following code is an example of a crossover subroutine. The subroutine creates two new offspring from two selected parents by switching the odd-numbered elements between the two parents.

   /* single-segment integer encoding of size 10 */
   call SetEncoding('I10');

   /* encoding size is 10 */
   n = 10;

   subroutine swapodd(selected[*], n);
     array child1[1] /nosym;
     array child2[1] /nosym;

     /* reallocate child arrays to right size */
     call dynamic_array(child1,n);
     call dynamic_array(child2,n);

     /* read segment 1 from parent 1 into child1 */
     call ReadParent(selected, 1, 1, child1);
   
     /* read segment 1 from parent 2 into child2 */
     call ReadParent(selected, 1, 2, child2);
   
     /* swap the odd elements in the solution */
     do i = 1 to n by 2;
       temp = child1[i];
       child1[i] = child2[i];
       child2[i] = temp;
     end;
   
     /* write offspring out to selected children */
     call WriteChild(selected, 1, 1, child1);
     call WriteChild(selected, 1, 2, child2);
   endsub;
   
   /* designate swapodd as the crossover routine */
   call SetCrossRoutine('swapodd');

The next sample program illustrates a crossover routine that might be used for multisegment mixed integer and sequence encoding. The subroutine uses the standard Simple crossover operator for the integer segment, and the Pmatch operator for the sequence-encoded segment.

   /* Solution has 2 segments, integer I5 and sequence S5 */
   call SetEncoding('I5S5');

   /* alpha parameter for Simple crossover operator */
   alpha = 1;

   subroutine mixedIS(selected[*], alpha);
  
     /* execute simple operator on segment 1 */
     call Cross(selected, 1, 'Simple', alpha);  

     /* execute pmatch operator on segment 2 */
     call Cross(selected, 2, 'Pmatch');

   endsub;

   call SetCrossRoutine('mixedIS');

For a mutation subroutine, use a ReadMember call to obtain the elements of the solution selected to be mutated, and use a WriteMember call to write the mutated elements back to the solution. For example, the following statements define a mutation subroutine that swaps two adjacent elements at a randomly chosen position in a sequence:

   /* Solution has 1 segment, sequence S10 */
   call SetEncoding('S10');
   
   n = 10;
   
   subroutine swap2(selected[*], n);
   
     /* declare an array for working memory */
     array member[1] /nosym;
   
     /* allocate array to required length */
     call dynamic_array(member, n);
     
     /* read segment 1 of selected member into array */
     call ReadMember(selected,1,member);
   
     /* generate random number between 0 and 1 */
     r = rand('uniform');
   
     /* convert r to integer between 1 and n-1 */
     i = int(r * (n - 1)) + 1;
   
     /* swap element values */
     temp = member[i];
     member[i] = member[i+1];
     member[i+1] = temp;
   
     /* write result back out to solution */
     call WriteMember(selected,1,member);
   
   endsub;
   
   /* Set the mutation routine to swap2 */
   call SetMutRoutine('swap2');