postgresql/src/backend/optimizer/geqo/geqo_erx.c

472 lines
11 KiB
C
Raw Normal View History

/*------------------------------------------------------------------------
*
* geqo_erx.c
* edge recombination crossover [ER]
*
2010-09-20 22:08:53 +02:00
* src/backend/optimizer/geqo/geqo_erx.c
*
*-------------------------------------------------------------------------
*/
/* contributed by:
=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=
* Martin Utesch * Institute of Automatic Control *
= = University of Mining and Technology =
* utesch@aut.tu-freiberg.de * Freiberg, Germany *
=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=
*/
/* the edge recombination algorithm is adopted from Genitor : */
/*************************************************************/
/* */
/* Copyright (c) 1990 */
/* Darrell L. Whitley */
/* Computer Science Department */
/* Colorado State University */
/* */
/* Permission is hereby granted to copy all or any part of */
/* this program for free distribution. The author's name */
/* and this copyright notice must be included in any copy. */
/* */
/*************************************************************/
#include "postgres.h"
#include "optimizer/geqo_random.h"
#include "optimizer/geqo_recombination.h"
#if defined(ERX)
static int gimme_edge(PlannerInfo *root, Gene gene1, Gene gene2, Edge *edge_table);
static void remove_gene(PlannerInfo *root, Gene gene, Edge edge, Edge *edge_table);
static Gene gimme_gene(PlannerInfo *root, Edge edge, Edge *edge_table);
static Gene edge_failure(PlannerInfo *root, Gene *gene, int index, Edge *edge_table, int num_gene);
/* alloc_edge_table
*
* allocate memory for edge table
*
*/
Edge *
alloc_edge_table(PlannerInfo *root, int num_gene)
{
Edge *edge_table;
/*
2005-10-15 04:49:52 +02:00
* palloc one extra location so that nodes numbered 1..n can be indexed
* directly; 0 will not be used
*/
edge_table = (Edge *) palloc((num_gene + 1) * sizeof(Edge));
1998-09-01 05:29:17 +02:00
return edge_table;
}
/* free_edge_table
*
* deallocate memory of edge table
*
*/
void
free_edge_table(PlannerInfo *root, Edge *edge_table)
{
pfree(edge_table);
}
/* gimme_edge_table
*
* fills a data structure which represents the set of explicit
* edges between points in the (2) input genes
*
* assumes circular tours and bidirectional edges
*
* gimme_edge() will set "shared" edges to negative values
*
* returns average number edges/city in range 2.0 - 4.0
* where 2.0=homogeneous; 4.0=diverse
*
*/
float
gimme_edge_table(PlannerInfo *root, Gene *tour1, Gene *tour2,
int num_gene, Edge *edge_table)
{
int i,
index1,
index2;
2005-10-15 04:49:52 +02:00
int edge_total; /* total number of unique edges in two genes */
/* at first clear the edge table's old data */
for (i = 1; i <= num_gene; i++)
{
edge_table[i].total_edges = 0;
edge_table[i].unused_edges = 0;
}
/* fill edge table with new data */
edge_total = 0;
for (index1 = 0; index1 < num_gene; index1++)
{
/*
* presume the tour is circular, i.e. 1->2, 2->3, 3->1 this operation
2005-10-15 04:49:52 +02:00
* maps n back to 1
*/
index2 = (index1 + 1) % num_gene;
/*
2005-10-15 04:49:52 +02:00
* edges are bidirectional, i.e. 1->2 is same as 2->1 call gimme_edge
* twice per edge
*/
edge_total += gimme_edge(root, tour1[index1], tour1[index2], edge_table);
gimme_edge(root, tour1[index2], tour1[index1], edge_table);
edge_total += gimme_edge(root, tour2[index1], tour2[index2], edge_table);
gimme_edge(root, tour2[index2], tour2[index1], edge_table);
}
/* return average number of edges per index */
1998-09-01 05:29:17 +02:00
return ((float) (edge_total * 2) / (float) num_gene);
}
/* gimme_edge
*
* registers edge from city1 to city2 in input edge table
*
* no assumptions about directionality are made;
* therefore it is up to the calling routine to
* call gimme_edge twice to make a bi-directional edge
* between city1 and city2;
* uni-directional edges are possible as well (just call gimme_edge
* once with the direction from city1 to city2)
*
* returns 1 if edge was not already registered and was just added;
* 0 if edge was already registered and edge_table is unchanged
*/
static int
gimme_edge(PlannerInfo *root, Gene gene1, Gene gene2, Edge *edge_table)
{
int i;
int edges;
int city1 = (int) gene1;
int city2 = (int) gene2;
/* check whether edge city1->city2 already exists */
edges = edge_table[city1].total_edges;
for (i = 0; i < edges; i++)
{
if ((Gene) Abs(edge_table[city1].edge_list[i]) == city2)
{
/* mark shared edges as negative */
edge_table[city1].edge_list[i] = 0 - city2;
1998-09-01 05:29:17 +02:00
return 0;
}
}
/* add city1->city2; */
edge_table[city1].edge_list[edges] = city2;
/* increment the number of edges from city1 */
edge_table[city1].total_edges++;
edge_table[city1].unused_edges++;
1998-09-01 05:29:17 +02:00
return 1;
}
/* gimme_tour
*
* creates a new tour using edges from the edge table.
* priority is given to "shared" edges (i.e. edges which
* all parent genes possess and are marked as negative
* in the edge table.)
*
*/
int
gimme_tour(PlannerInfo *root, Edge *edge_table, Gene *new_gene, int num_gene)
{
int i;
int edge_failures = 0;
/* choose int between 1 and num_gene */
new_gene[0] = (Gene) geqo_randint(root, num_gene, 1);
for (i = 1; i < num_gene; i++)
{
/*
* as each point is entered into the tour, remove it from the edge
* table
*/
remove_gene(root, new_gene[i - 1], edge_table[(int) new_gene[i - 1]], edge_table);
/* find destination for the newly entered point */
if (edge_table[new_gene[i - 1]].unused_edges > 0)
new_gene[i] = gimme_gene(root, edge_table[(int) new_gene[i - 1]], edge_table);
else
{ /* cope with fault */
edge_failures++;
new_gene[i] = edge_failure(root, new_gene, i - 1, edge_table, num_gene);
}
/* mark this node as incorporated */
edge_table[(int) new_gene[i - 1]].unused_edges = -1;
} /* for (i=1; i<num_gene; i++) */
1998-09-01 05:29:17 +02:00
return edge_failures;
}
/* remove_gene
*
* removes input gene from edge_table.
* input edge is used
* to identify deletion locations within edge table.
*
*/
static void
remove_gene(PlannerInfo *root, Gene gene, Edge edge, Edge *edge_table)
{
int i,
j;
int possess_edge;
int genes_remaining;
/*
* do for every gene known to have an edge to input gene (i.e. in
* edge_list for input edge)
*/
for (i = 0; i < edge.unused_edges; i++)
{
possess_edge = (int) Abs(edge.edge_list[i]);
genes_remaining = edge_table[possess_edge].unused_edges;
/* find the input gene in all edge_lists and delete it */
for (j = 0; j < genes_remaining; j++)
{
if ((Gene) Abs(edge_table[possess_edge].edge_list[j]) == gene)
{
edge_table[possess_edge].unused_edges--;
edge_table[possess_edge].edge_list[j] =
edge_table[possess_edge].edge_list[genes_remaining - 1];
break;
}
}
}
}
/* gimme_gene
*
* priority is given to "shared" edges
* (i.e. edges which both genes possess)
*
*/
static Gene
gimme_gene(PlannerInfo *root, Edge edge, Edge *edge_table)
{
int i;
Gene friend;
int minimum_edges;
int minimum_count = -1;
int rand_decision;
/*
* no point has edges to more than 4 other points thus, this contrived
* minimum will be replaced
*/
minimum_edges = 5;
/* consider candidate destination points in edge list */
for (i = 0; i < edge.unused_edges; i++)
{
friend = (Gene) edge.edge_list[i];
/*
* give priority to shared edges that are negative; so return 'em
*/
/*
* negative values are caught here so we need not worry about
* converting to absolute values
*/
if (friend < 0)
1998-09-01 05:29:17 +02:00
return (Gene) Abs(friend);
/*
* give priority to candidates with fewest remaining unused edges;
* find out what the minimum number of unused edges is
* (minimum_edges); if there is more than one candidate with the
* minimum number of unused edges keep count of this number
* (minimum_count);
*/
/*
2005-10-15 04:49:52 +02:00
* The test for minimum_count can probably be removed at some point
* but comments should probably indicate exactly why it is guaranteed
* that the test will always succeed the first time around. If it can
* fail then the code is in error
*/
if (edge_table[(int) friend].unused_edges < minimum_edges)
{
minimum_edges = edge_table[(int) friend].unused_edges;
minimum_count = 1;
}
else if (minimum_count == -1)
elog(ERROR, "minimum_count not set");
else if (edge_table[(int) friend].unused_edges == minimum_edges)
minimum_count++;
} /* for (i=0; i<edge.unused_edges; i++) */
/* random decision of the possible candidates to use */
rand_decision = geqo_randint(root, minimum_count - 1, 0);
for (i = 0; i < edge.unused_edges; i++)
{
friend = (Gene) edge.edge_list[i];
/* return the chosen candidate point */
if (edge_table[(int) friend].unused_edges == minimum_edges)
{
minimum_count--;
if (minimum_count == rand_decision)
1998-09-01 05:29:17 +02:00
return friend;
}
}
/* ... should never be reached */
elog(ERROR, "neither shared nor minimum number nor random edge found");
return 0; /* to keep the compiler quiet */
}
/* edge_failure
*
* routine for handling edge failure
*
*/
static Gene
edge_failure(PlannerInfo *root, Gene *gene, int index, Edge *edge_table, int num_gene)
{
int i;
Gene fail_gene = gene[index];
int remaining_edges = 0;
int four_count = 0;
int rand_decision;
/*
2005-10-15 04:49:52 +02:00
* how many edges remain? how many gene with four total (initial) edges
* remain?
*/
for (i = 1; i <= num_gene; i++)
{
if ((edge_table[i].unused_edges != -1) && (i != (int) fail_gene))
{
remaining_edges++;
if (edge_table[i].total_edges == 4)
four_count++;
}
}
/*
2005-10-15 04:49:52 +02:00
* random decision of the gene with remaining edges and whose total_edges
* == 4
*/
if (four_count != 0)
{
rand_decision = geqo_randint(root, four_count - 1, 0);
for (i = 1; i <= num_gene; i++)
{
if ((Gene) i != fail_gene &&
edge_table[i].unused_edges != -1 &&
edge_table[i].total_edges == 4)
{
four_count--;
if (rand_decision == four_count)
1998-09-01 05:29:17 +02:00
return (Gene) i;
}
}
elog(LOG, "no edge found via random decision and total_edges == 4");
}
else if (remaining_edges != 0)
{
/* random decision of the gene with remaining edges */
rand_decision = geqo_randint(root, remaining_edges - 1, 0);
for (i = 1; i <= num_gene; i++)
{
if ((Gene) i != fail_gene &&
edge_table[i].unused_edges != -1)
{
remaining_edges--;
if (rand_decision == remaining_edges)
1998-09-01 05:29:17 +02:00
return i;
}
}
elog(LOG, "no edge found via random decision with remaining edges");
}
/*
2005-10-15 04:49:52 +02:00
* edge table seems to be empty; this happens sometimes on the last point
* due to the fact that the first point is removed from the table even
* though only one of its edges has been determined
*/
else
2005-10-15 04:49:52 +02:00
{ /* occurs only at the last point in the tour;
* simply look for the point which is not yet
* used */
for (i = 1; i <= num_gene; i++)
if (edge_table[i].unused_edges >= 0)
1998-09-01 05:29:17 +02:00
return (Gene) i;
elog(LOG, "no edge found via looking for the last unused point");
}
/* ... should never be reached */
elog(ERROR, "no edge found");
return 0; /* to keep the compiler quiet */
}
Phase 2 of pgindent updates. Change pg_bsd_indent to follow upstream rules for placement of comments to the right of code, and remove pgindent hack that caused comments following #endif to not obey the general rule. Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using the published version of pg_bsd_indent, but a hacked-up version that tried to minimize the amount of movement of comments to the right of code. The situation of interest is where such a comment has to be moved to the right of its default placement at column 33 because there's code there. BSD indent has always moved right in units of tab stops in such cases --- but in the previous incarnation, indent was working in 8-space tab stops, while now it knows we use 4-space tabs. So the net result is that in about half the cases, such comments are placed one tab stop left of before. This is better all around: it leaves more room on the line for comment text, and it means that in such cases the comment uniformly starts at the next 4-space tab stop after the code, rather than sometimes one and sometimes two tabs after. Also, ensure that comments following #endif are indented the same as comments following other preprocessor commands such as #else. That inconsistency turns out to have been self-inflicted damage from a poorly-thought-through post-indent "fixup" in pgindent. This patch is much less interesting than the first round of indent changes, but also bulkier, so I thought it best to separate the effects. Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:18:54 +02:00
#endif /* defined(ERX) */