summaryrefslogtreecommitdiffstats
path: root/src/backend/optimizer/geqo/geqo_erx.c
blob: 3b92f420e0aaa9740645a39c56507ea3a69558d4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
/*------------------------------------------------------------------------
*
* geqo_erx.c
*	 edge recombination crossover [ER]
*
* 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;

	/*
	 * 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));

	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;
	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
		 * maps n back to 1
		 */

		index2 = (index1 + 1) % num_gene;

		/*
		 * 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 */
	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;

			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++;

	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++) */

	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)
			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);
		 */

		/*
		 * 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)
				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;


	/*
	 * 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++;
		}
	}

	/*
	 * 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)
					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)
					return i;
			}
		}

		elog(LOG, "no edge found via random decision with remaining edges");
	}

	/*
	 * 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
	{							/* 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)
				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 */
}

#endif							/* defined(ERX) */