1 | /* PGAPack test program. |
---|
2 | * |
---|
3 | * The objective is to evolve a string of characters to match a string |
---|
4 | * supplied by the user. We will stop evolving when either we run out |
---|
5 | * of iterations (500), or when the best string has the same evaluation |
---|
6 | * value for 100 generations. |
---|
7 | * |
---|
8 | * One problem with this implementation is that ' ' is not in |
---|
9 | * PGA_DATATYPE_CHAR if we limit it using PGA_CINIT_MIXED, PGA_CINIT_LOWER, |
---|
10 | * or PGA_CINIT_UPPER. To fix this, we must define our own interval, and |
---|
11 | * thus, our own mutation, initialization operators. |
---|
12 | * |
---|
13 | * A user function is also used to check the "done" condition; we are |
---|
14 | * done if we've done more than 1000 iterations, or the evolved string |
---|
15 | * is correct. |
---|
16 | * |
---|
17 | * Created 28 Sep 95, Brian P. Walenz. Thanks to Dan Ashlock for the idea. |
---|
18 | * |
---|
19 | * Be warned that duplicate checking will sometimes go into an infinite |
---|
20 | * loop. |
---|
21 | */ |
---|
22 | |
---|
23 | #include "pgapack.h" |
---|
24 | |
---|
25 | void N_InitString (PGAContext *, int, int); |
---|
26 | int N_Mutation (PGAContext *, int, int, double); |
---|
27 | int N_StopCond (PGAContext *); |
---|
28 | void N_Crossover (PGAContext *ctx, int, int, int, int, int, int); |
---|
29 | int N_Duplicate (PGAContext *, int, int, int, int); |
---|
30 | void N_PrintString(PGAContext *, FILE *, int, int); |
---|
31 | void N_EndOfGen (PGAContext *); |
---|
32 | double EvalName (PGAContext *, int, int); |
---|
33 | void GetStringParameter(char *, char *); |
---|
34 | |
---|
35 | /* Global, because we use it in EvalName. */ |
---|
36 | char Name[70]; |
---|
37 | |
---|
38 | void main(int argc, char **argv) { |
---|
39 | PGAContext *ctx; |
---|
40 | |
---|
41 | MPI_Init(&argc, &argv); |
---|
42 | |
---|
43 | /* Rather than deal with standard io and strings, we'll just set |
---|
44 | * this explicitly. |
---|
45 | */ |
---|
46 | strcpy(Name, "David M. Levine, Philip L. Hallstrom, David M. Noelle, " |
---|
47 | "Brian P. Walenz"); |
---|
48 | |
---|
49 | ctx = PGACreate(&argc, argv, PGA_DATATYPE_CHARACTER, strlen(Name), |
---|
50 | PGA_MAXIMIZE); |
---|
51 | |
---|
52 | PGASetRandomSeed(ctx, 42); |
---|
53 | |
---|
54 | PGASetUserFunction(ctx, PGA_USERFUNCTION_INITSTRING, (void *)N_InitString); |
---|
55 | PGASetUserFunction(ctx, PGA_USERFUNCTION_MUTATION, (void *)N_Mutation); |
---|
56 | PGASetUserFunction(ctx, PGA_USERFUNCTION_CROSSOVER, (void *)N_Crossover); |
---|
57 | PGASetUserFunction(ctx, PGA_USERFUNCTION_DUPLICATE, (void *)N_Duplicate); |
---|
58 | PGASetUserFunction(ctx, PGA_USERFUNCTION_STOPCOND, (void *)N_StopCond); |
---|
59 | PGASetUserFunction(ctx, PGA_USERFUNCTION_PRINTSTRING,(void *)N_PrintString); |
---|
60 | PGASetUserFunction(ctx, PGA_USERFUNCTION_ENDOFGEN, (void *)N_EndOfGen); |
---|
61 | |
---|
62 | /* We don't want to report anything. */ |
---|
63 | PGASetPrintFrequencyValue(ctx, 10000); |
---|
64 | PGASetPopSize(ctx, 100); |
---|
65 | PGASetNumReplaceValue(ctx, 90); |
---|
66 | PGASetPopReplaceType(ctx, PGA_POPREPL_BEST); |
---|
67 | PGASetNoDuplicatesFlag(ctx, PGA_TRUE); |
---|
68 | PGASetMaxGAIterValue(ctx, 100); |
---|
69 | |
---|
70 | PGASetUp(ctx); |
---|
71 | PGARun(ctx, EvalName); |
---|
72 | PGADestroy(ctx); |
---|
73 | |
---|
74 | MPI_Finalize(); |
---|
75 | } |
---|
76 | |
---|
77 | |
---|
78 | /* Function to randomly initialize a PGA_DATATYPE_CHARACTER string using |
---|
79 | * all printable ASCII characters for the range. |
---|
80 | */ |
---|
81 | void N_InitString(PGAContext *ctx, int p, int pop) { |
---|
82 | int i; |
---|
83 | |
---|
84 | for(i=PGAGetStringLength(ctx)-1; i>=0; i--) |
---|
85 | PGASetCharacterAllele(ctx, p, pop, i, |
---|
86 | PGARandomInterval(ctx, 32, 126)); |
---|
87 | } |
---|
88 | |
---|
89 | |
---|
90 | |
---|
91 | /* Function to crossover two name strings. Quite an interesting |
---|
92 | * crossover, too. Works like a normal uniform crossover, except |
---|
93 | * that, if one of the strings matches the correct value, we set |
---|
94 | * BOTH children to the correct value 50% of the time. |
---|
95 | */ |
---|
96 | void N_Crossover(PGAContext *ctx, int p1, int p2, int pop1, int c1, |
---|
97 | int c2, int pop2) { |
---|
98 | int i, length; |
---|
99 | char a, b; |
---|
100 | |
---|
101 | length = PGAGetStringLength(ctx); |
---|
102 | |
---|
103 | for (i=0; i<length; i++) { |
---|
104 | a = PGAGetCharacterAllele(ctx, p1, pop1, i); |
---|
105 | b = PGAGetCharacterAllele(ctx, p2, pop1, i); |
---|
106 | if ((a == Name[i]) || (b == Name[i])) |
---|
107 | a = b = Name[i]; |
---|
108 | |
---|
109 | if (PGARandomFlip(ctx, 0.5) == PGA_TRUE) { |
---|
110 | PGASetCharacterAllele(ctx, c1, pop2, i, a); |
---|
111 | PGASetCharacterAllele(ctx, c2, pop2, i, b); |
---|
112 | } else { |
---|
113 | PGASetCharacterAllele(ctx, c1, pop2, i, b); |
---|
114 | PGASetCharacterAllele(ctx, c2, pop2, i, a); |
---|
115 | } |
---|
116 | } |
---|
117 | } |
---|
118 | |
---|
119 | |
---|
120 | |
---|
121 | /* Function to compare two strings. Strings are "equalivalent" |
---|
122 | * if they match Name at the same alleles (and, thus, disagree at the |
---|
123 | * same alleles). We don't care what the disagreement is, just that |
---|
124 | * it is there. |
---|
125 | * |
---|
126 | * NOTE that because it is possible to get stuck in an infinite |
---|
127 | * loop while doing duplicate checking on this string (assuming that |
---|
128 | * the mutation operator is always beneficial), we ALWAYS return PGA_FALSE. |
---|
129 | * The code is left as an example (and for testing). |
---|
130 | */ |
---|
131 | int N_Duplicate(PGAContext *ctx, int p1, int pop1, int p2, int pop2) { |
---|
132 | int i, match; |
---|
133 | char a, b, c; |
---|
134 | |
---|
135 | match = PGA_TRUE; |
---|
136 | |
---|
137 | for (i=PGAGetStringLength(ctx)-1; match && i>=0; i--) { |
---|
138 | a = PGAGetCharacterAllele(ctx, p1, pop1, i); |
---|
139 | b = PGAGetCharacterAllele(ctx, p2, pop2, i); |
---|
140 | c = Name[i]; |
---|
141 | if (((a == c) && (b != c)) || ((a != c) && (b == c))) |
---|
142 | match = PGA_FALSE; |
---|
143 | } |
---|
144 | |
---|
145 | return(match); |
---|
146 | } |
---|
147 | |
---|
148 | |
---|
149 | /* Function to muatate a PGA_DATATYPE_CHARACTER string. This is done |
---|
150 | * by simply picking allele locations and replacing whatever was there. |
---|
151 | * Again, legal values are all printable ASCII characters. |
---|
152 | */ |
---|
153 | int N_Mutation(PGAContext *ctx, int p, int pop, double mr) { |
---|
154 | int i, count; |
---|
155 | |
---|
156 | count = 0; |
---|
157 | for(i=PGAGetStringLength(ctx)-1; i>=0; i--) |
---|
158 | if (PGAGetCharacterAllele(ctx, p, pop, i) != Name[i]) { |
---|
159 | if (PGARandomFlip(ctx, mr) == PGA_TRUE) { |
---|
160 | PGASetCharacterAllele(ctx, p, pop, i, |
---|
161 | PGARandomInterval(ctx, 32, 126)); |
---|
162 | count++; |
---|
163 | } |
---|
164 | } |
---|
165 | return(count); |
---|
166 | } |
---|
167 | |
---|
168 | |
---|
169 | /* Function to print a string. Since fortran does NOT support |
---|
170 | * C file handles, we just print normally. If we we're in C, |
---|
171 | * we would print to the file "file". |
---|
172 | */ |
---|
173 | void N_PrintString(PGAContext *ctx, FILE *file, int p, int pop) { |
---|
174 | int i; |
---|
175 | char string[71]; |
---|
176 | |
---|
177 | for (i=PGAGetStringLength(ctx)-1; i>=0; i--) |
---|
178 | string[i] = PGAGetCharacterAllele(ctx, p, pop, i); |
---|
179 | string[70] = 0; |
---|
180 | |
---|
181 | fprintf(file," :%s:\n", string); |
---|
182 | } |
---|
183 | |
---|
184 | |
---|
185 | /* Function to check "doneness" of the GA. We check the iteration |
---|
186 | * count (by calling PGACheckStoppingConditions), then check if we have found |
---|
187 | * the string yet. |
---|
188 | */ |
---|
189 | int N_StopCond(PGAContext *ctx) { |
---|
190 | int done, best; |
---|
191 | |
---|
192 | done = PGACheckStoppingConditions(ctx); |
---|
193 | |
---|
194 | best = PGAGetBestIndex(ctx, PGA_OLDPOP); |
---|
195 | if ((done == PGA_FALSE) && |
---|
196 | (PGAGetEvaluation(ctx, best, PGA_OLDPOP) == |
---|
197 | PGAGetStringLength(ctx))) |
---|
198 | done = PGA_TRUE; |
---|
199 | |
---|
200 | return(done); |
---|
201 | } |
---|
202 | |
---|
203 | |
---|
204 | |
---|
205 | /* After each generation, this routine is called. What is done here, |
---|
206 | * is to print the best string in our own format, then check if the |
---|
207 | * best string is close to the correct value. If it is, duplicate |
---|
208 | * checking is tunred off. This is critical, as the mutation operator |
---|
209 | * will not degrade a string, so when the strings get near the correct |
---|
210 | * solution, they all become duplicates, but none can be changed! |
---|
211 | * |
---|
212 | * Other applications have done such things as send the best string |
---|
213 | * to another process to be visualized. For here, we just call our |
---|
214 | * print string function to print the best string. |
---|
215 | */ |
---|
216 | void N_EndOfGen(PGAContext *ctx) { |
---|
217 | int best; |
---|
218 | |
---|
219 | best = PGAGetBestIndex(ctx, PGA_NEWPOP); |
---|
220 | N_PrintString(ctx, stdout, best, PGA_NEWPOP); |
---|
221 | |
---|
222 | if (PGAGetEvaluation(ctx, best, PGA_NEWPOP) >= |
---|
223 | PGAGetStringLength(ctx)-10) |
---|
224 | PGASetNoDuplicatesFlag(ctx, PGA_FALSE); |
---|
225 | } |
---|
226 | |
---|
227 | |
---|
228 | /* Evaluate the string. A highly fit string will have many of |
---|
229 | * the characters matching Name. |
---|
230 | */ |
---|
231 | double EvalName(PGAContext *ctx, int p, int pop) { |
---|
232 | int i, count; |
---|
233 | |
---|
234 | count = 0; |
---|
235 | for (i=PGAGetStringLength(ctx)-1; i>=0; i--) { |
---|
236 | if (PGAGetCharacterAllele(ctx, p, pop, i) == Name[i]) |
---|
237 | count++; |
---|
238 | } |
---|
239 | |
---|
240 | return((double)count); |
---|
241 | } |
---|
242 | |
---|