Header And Logo

PostgreSQL
| The world's most advanced open source database.

geqo_erx.c

Go to the documentation of this file.
00001 /*------------------------------------------------------------------------
00002 *
00003 * geqo_erx.c
00004 *    edge recombination crossover [ER]
00005 *
00006 * src/backend/optimizer/geqo/geqo_erx.c
00007 *
00008 *-------------------------------------------------------------------------
00009 */
00010 
00011 /* contributed by:
00012    =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=
00013    *  Martin Utesch              * Institute of Automatic Control      *
00014    =                             = University of Mining and Technology =
00015    *  [email protected]  * Freiberg, Germany                   *
00016    =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=
00017  */
00018 
00019 /* the edge recombination algorithm is adopted from Genitor : */
00020 /*************************************************************/
00021 /*                                                           */
00022 /*  Copyright (c) 1990                                       */
00023 /*  Darrell L. Whitley                                       */
00024 /*  Computer Science Department                              */
00025 /*  Colorado State University                                */
00026 /*                                                           */
00027 /*  Permission is hereby granted to copy all or any part of  */
00028 /*  this program for free distribution.   The author's name  */
00029 /*  and this copyright notice must be included in any copy.  */
00030 /*                                                           */
00031 /*************************************************************/
00032 
00033 
00034 #include "postgres.h"
00035 #include "optimizer/geqo_recombination.h"
00036 #include "optimizer/geqo_random.h"
00037 
00038 
00039 static int  gimme_edge(PlannerInfo *root, Gene gene1, Gene gene2, Edge *edge_table);
00040 static void remove_gene(PlannerInfo *root, Gene gene, Edge edge, Edge *edge_table);
00041 static Gene gimme_gene(PlannerInfo *root, Edge edge, Edge *edge_table);
00042 
00043 static Gene edge_failure(PlannerInfo *root, Gene *gene, int index, Edge *edge_table, int num_gene);
00044 
00045 
00046 /* alloc_edge_table
00047  *
00048  *   allocate memory for edge table
00049  *
00050  */
00051 
00052 Edge *
00053 alloc_edge_table(PlannerInfo *root, int num_gene)
00054 {
00055     Edge       *edge_table;
00056 
00057     /*
00058      * palloc one extra location so that nodes numbered 1..n can be indexed
00059      * directly; 0 will not be used
00060      */
00061 
00062     edge_table = (Edge *) palloc((num_gene + 1) * sizeof(Edge));
00063 
00064     return edge_table;
00065 }
00066 
00067 /* free_edge_table
00068  *
00069  *    deallocate memory of edge table
00070  *
00071  */
00072 void
00073 free_edge_table(PlannerInfo *root, Edge *edge_table)
00074 {
00075     pfree(edge_table);
00076 }
00077 
00078 /* gimme_edge_table
00079  *
00080  *   fills a data structure which represents the set of explicit
00081  *   edges between points in the (2) input genes
00082  *
00083  *   assumes circular tours and bidirectional edges
00084  *
00085  *   gimme_edge() will set "shared" edges to negative values
00086  *
00087  *   returns average number edges/city in range 2.0 - 4.0
00088  *   where 2.0=homogeneous; 4.0=diverse
00089  *
00090  */
00091 float
00092 gimme_edge_table(PlannerInfo *root, Gene *tour1, Gene *tour2,
00093                  int num_gene, Edge *edge_table)
00094 {
00095     int         i,
00096                 index1,
00097                 index2;
00098     int         edge_total;     /* total number of unique edges in two genes */
00099 
00100     /* at first clear the edge table's old data */
00101     for (i = 1; i <= num_gene; i++)
00102     {
00103         edge_table[i].total_edges = 0;
00104         edge_table[i].unused_edges = 0;
00105     }
00106 
00107     /* fill edge table with new data */
00108 
00109     edge_total = 0;
00110 
00111     for (index1 = 0; index1 < num_gene; index1++)
00112     {
00113         /*
00114          * presume the tour is circular, i.e. 1->2, 2->3, 3->1 this operaton
00115          * maps n back to 1
00116          */
00117 
00118         index2 = (index1 + 1) % num_gene;
00119 
00120         /*
00121          * edges are bidirectional, i.e. 1->2 is same as 2->1 call gimme_edge
00122          * twice per edge
00123          */
00124 
00125         edge_total += gimme_edge(root, tour1[index1], tour1[index2], edge_table);
00126         gimme_edge(root, tour1[index2], tour1[index1], edge_table);
00127 
00128         edge_total += gimme_edge(root, tour2[index1], tour2[index2], edge_table);
00129         gimme_edge(root, tour2[index2], tour2[index1], edge_table);
00130     }
00131 
00132     /* return average number of edges per index */
00133     return ((float) (edge_total * 2) / (float) num_gene);
00134 }
00135 
00136 /* gimme_edge
00137  *
00138  *    registers edge from city1 to city2 in input edge table
00139  *
00140  *    no assumptions about directionality are made;
00141  *    therefor it is up to the calling routine to
00142  *    call gimme_edge twice to make a bi-directional edge
00143  *    between city1 and city2;
00144  *    uni-directional edges are possible as well (just call gimme_edge
00145  *    once with the direction from city1 to city2)
00146  *
00147  *    returns 1 if edge was not already registered and was just added;
00148  *            0 if edge was already registered and edge_table is unchanged
00149  */
00150 static int
00151 gimme_edge(PlannerInfo *root, Gene gene1, Gene gene2, Edge *edge_table)
00152 {
00153     int         i;
00154     int         edges;
00155     int         city1 = (int) gene1;
00156     int         city2 = (int) gene2;
00157 
00158 
00159     /* check whether edge city1->city2 already exists */
00160     edges = edge_table[city1].total_edges;
00161 
00162     for (i = 0; i < edges; i++)
00163     {
00164         if ((Gene) Abs(edge_table[city1].edge_list[i]) == city2)
00165         {
00166 
00167             /* mark shared edges as negative */
00168             edge_table[city1].edge_list[i] = 0 - city2;
00169 
00170             return 0;
00171         }
00172     }
00173 
00174     /* add city1->city2; */
00175     edge_table[city1].edge_list[edges] = city2;
00176 
00177     /* increment the number of edges from city1 */
00178     edge_table[city1].total_edges++;
00179     edge_table[city1].unused_edges++;
00180 
00181     return 1;
00182 }
00183 
00184 /* gimme_tour
00185  *
00186  *    creates a new tour using edges from the edge table.
00187  *    priority is given to "shared" edges (i.e. edges which
00188  *    all parent genes possess and are marked as negative
00189  *    in the edge table.)
00190  *
00191  */
00192 int
00193 gimme_tour(PlannerInfo *root, Edge *edge_table, Gene *new_gene, int num_gene)
00194 {
00195     int         i;
00196     int         edge_failures = 0;
00197 
00198     /* choose int between 1 and num_gene */
00199     new_gene[0] = (Gene) geqo_randint(root, num_gene, 1);
00200 
00201     for (i = 1; i < num_gene; i++)
00202     {
00203         /*
00204          * as each point is entered into the tour, remove it from the edge
00205          * table
00206          */
00207 
00208         remove_gene(root, new_gene[i - 1], edge_table[(int) new_gene[i - 1]], edge_table);
00209 
00210         /* find destination for the newly entered point */
00211 
00212         if (edge_table[new_gene[i - 1]].unused_edges > 0)
00213             new_gene[i] = gimme_gene(root, edge_table[(int) new_gene[i - 1]], edge_table);
00214 
00215         else
00216         {                       /* cope with fault */
00217             edge_failures++;
00218 
00219             new_gene[i] = edge_failure(root, new_gene, i - 1, edge_table, num_gene);
00220         }
00221 
00222         /* mark this node as incorporated */
00223         edge_table[(int) new_gene[i - 1]].unused_edges = -1;
00224 
00225     }                           /* for (i=1; i<num_gene; i++) */
00226 
00227     return edge_failures;
00228 
00229 }
00230 
00231 /* remove_gene
00232  *
00233  *   removes input gene from edge_table.
00234  *   input edge is used
00235  *   to identify deletion locations within edge table.
00236  *
00237  */
00238 static void
00239 remove_gene(PlannerInfo *root, Gene gene, Edge edge, Edge *edge_table)
00240 {
00241     int         i,
00242                 j;
00243     int         possess_edge;
00244     int         genes_remaining;
00245 
00246     /*
00247      * do for every gene known to have an edge to input gene (i.e. in
00248      * edge_list for input edge)
00249      */
00250 
00251     for (i = 0; i < edge.unused_edges; i++)
00252     {
00253         possess_edge = (int) Abs(edge.edge_list[i]);
00254         genes_remaining = edge_table[possess_edge].unused_edges;
00255 
00256         /* find the input gene in all edge_lists and delete it */
00257         for (j = 0; j < genes_remaining; j++)
00258         {
00259 
00260             if ((Gene) Abs(edge_table[possess_edge].edge_list[j]) == gene)
00261             {
00262 
00263                 edge_table[possess_edge].unused_edges--;
00264 
00265                 edge_table[possess_edge].edge_list[j] =
00266                     edge_table[possess_edge].edge_list[genes_remaining - 1];
00267 
00268                 break;
00269             }
00270         }
00271     }
00272 }
00273 
00274 /* gimme_gene
00275  *
00276  *    priority is given to "shared" edges
00277  *    (i.e. edges which both genes possess)
00278  *
00279  */
00280 static Gene
00281 gimme_gene(PlannerInfo *root, Edge edge, Edge *edge_table)
00282 {
00283     int         i;
00284     Gene        friend;
00285     int         minimum_edges;
00286     int         minimum_count = -1;
00287     int         rand_decision;
00288 
00289     /*
00290      * no point has edges to more than 4 other points thus, this contrived
00291      * minimum will be replaced
00292      */
00293 
00294     minimum_edges = 5;
00295 
00296     /* consider candidate destination points in edge list */
00297 
00298     for (i = 0; i < edge.unused_edges; i++)
00299     {
00300         friend = (Gene) edge.edge_list[i];
00301 
00302         /*
00303          * give priority to shared edges that are negative; so return 'em
00304          */
00305 
00306         /*
00307          * negative values are caught here so we need not worry about
00308          * converting to absolute values
00309          */
00310         if (friend < 0)
00311             return (Gene) Abs(friend);
00312 
00313 
00314         /*
00315          * give priority to candidates with fewest remaining unused edges;
00316          * find out what the minimum number of unused edges is
00317          * (minimum_edges); if there is more than one cadidate with the
00318          * minimum number of unused edges keep count of this number
00319          * (minimum_count);
00320          */
00321 
00322         /*
00323          * The test for minimum_count can probably be removed at some point
00324          * but comments should probably indicate exactly why it is guaranteed
00325          * that the test will always succeed the first time around.  If it can
00326          * fail then the code is in error
00327          */
00328 
00329 
00330         if (edge_table[(int) friend].unused_edges < minimum_edges)
00331         {
00332             minimum_edges = edge_table[(int) friend].unused_edges;
00333             minimum_count = 1;
00334         }
00335         else if (minimum_count == -1)
00336             elog(ERROR, "minimum_count not set");
00337         else if (edge_table[(int) friend].unused_edges == minimum_edges)
00338             minimum_count++;
00339 
00340     }                           /* for (i=0; i<edge.unused_edges; i++) */
00341 
00342 
00343     /* random decision of the possible candidates to use */
00344     rand_decision = geqo_randint(root, minimum_count - 1, 0);
00345 
00346 
00347     for (i = 0; i < edge.unused_edges; i++)
00348     {
00349         friend = (Gene) edge.edge_list[i];
00350 
00351         /* return the chosen candidate point */
00352         if (edge_table[(int) friend].unused_edges == minimum_edges)
00353         {
00354             minimum_count--;
00355 
00356             if (minimum_count == rand_decision)
00357                 return friend;
00358         }
00359     }
00360 
00361     /* ... should never be reached */
00362     elog(ERROR, "neither shared nor minimum number nor random edge found");
00363     return 0;                   /* to keep the compiler quiet */
00364 }
00365 
00366 /* edge_failure
00367  *
00368  *    routine for handling edge failure
00369  *
00370  */
00371 static Gene
00372 edge_failure(PlannerInfo *root, Gene *gene, int index, Edge *edge_table, int num_gene)
00373 {
00374     int         i;
00375     Gene        fail_gene = gene[index];
00376     int         remaining_edges = 0;
00377     int         four_count = 0;
00378     int         rand_decision;
00379 
00380 
00381     /*
00382      * how many edges remain? how many gene with four total (initial) edges
00383      * remain?
00384      */
00385 
00386     for (i = 1; i <= num_gene; i++)
00387     {
00388         if ((edge_table[i].unused_edges != -1) && (i != (int) fail_gene))
00389         {
00390             remaining_edges++;
00391 
00392             if (edge_table[i].total_edges == 4)
00393                 four_count++;
00394         }
00395     }
00396 
00397     /*
00398      * random decision of the gene with remaining edges and whose total_edges
00399      * == 4
00400      */
00401 
00402     if (four_count != 0)
00403     {
00404 
00405         rand_decision = geqo_randint(root, four_count - 1, 0);
00406 
00407         for (i = 1; i <= num_gene; i++)
00408         {
00409 
00410             if ((Gene) i != fail_gene &&
00411                 edge_table[i].unused_edges != -1 &&
00412                 edge_table[i].total_edges == 4)
00413             {
00414 
00415                 four_count--;
00416 
00417                 if (rand_decision == four_count)
00418                     return (Gene) i;
00419             }
00420         }
00421 
00422         elog(LOG, "no edge found via random decision and total_edges == 4");
00423     }
00424     else if (remaining_edges != 0)
00425     {
00426         /* random decision of the gene with remaining edges */
00427         rand_decision = geqo_randint(root, remaining_edges - 1, 0);
00428 
00429         for (i = 1; i <= num_gene; i++)
00430         {
00431 
00432             if ((Gene) i != fail_gene &&
00433                 edge_table[i].unused_edges != -1)
00434             {
00435 
00436                 remaining_edges--;
00437 
00438                 if (rand_decision == remaining_edges)
00439                     return i;
00440             }
00441         }
00442 
00443         elog(LOG, "no edge found via random decision with remaining edges");
00444     }
00445 
00446     /*
00447      * edge table seems to be empty; this happens sometimes on the last point
00448      * due to the fact that the first point is removed from the table even
00449      * though only one of its edges has been determined
00450      */
00451 
00452     else
00453     {                           /* occurs only at the last point in the tour;
00454                                  * simply look for the point which is not yet
00455                                  * used */
00456 
00457         for (i = 1; i <= num_gene; i++)
00458             if (edge_table[i].unused_edges >= 0)
00459                 return (Gene) i;
00460 
00461         elog(LOG, "no edge found via looking for the last ununsed point");
00462     }
00463 
00464 
00465     /* ... should never be reached */
00466     elog(ERROR, "no edge found");
00467     return 0;                   /* to keep the compiler quiet */
00468 }