Advertisement
Guest User

Untitled

a guest
Feb 23rd, 2016
104
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.03 KB | None | 0 0
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <time.h>
  5. #include <unistd.h>
  6. #include <sys/select.h>
  7. #include <termios.h>
  8. #include <ctype.h>
  9. #include <math.h>
  10.  
  11. // adjustable parameters
  12. #define POPULATION_SIZE 50000 // total population size
  13. #define NUM_SURVIVORS 1 // number of survivors per generation (must be less than POPULATION_SIZE)
  14. #define GENOME_LEN 28 // number of loci in each genome (each locus is one character)
  15. #define MUTATION_RATE 0.01 // probability that a locus is mutated per generation
  16. #define SELECTION_COEFF 5.0 // selection coefficient
  17. #define DISPLAY_INTERVAL 10 // display results every n generations, where n == DISPLAY_INTERVAL
  18. #define PAUSE_TIME 50000 // pause time in microseconds
  19. #define GENOMES_TO_DISPLAY 1 // in STEP_MODE, number of genomes to display at each step
  20. #define STEP_MODE 1 // if set, program will pause every n generations and display the genomes
  21. #define ENABLE_LATCHING 0 // if set, enable latching
  22. #define ENABLE_SELECTION 1 // if set, enable selection; otherwise, select survivors with no bias
  23.  
  24. #define CHARSET_SIZE 27 // 26 uppercase letters plus the space character
  25. #define NUM_LINES 300 // number of lines in the program's display "window"
  26. #define CHARS_PER_LINE 120 // for each line in the "window"
  27.  
  28. long long generation; // generation count
  29. long long histogram[GENOME_LEN+1]; // histogram of Hamming distance from the target
  30. double mutation_rate;
  31. double selection_coefficient;
  32. double floating_num_survivors;
  33. int num_survivors;
  34. char buffer[20];
  35.  
  36. // A genome consists of an array of loci, the associated fitness value, and a field indicating the number of matches
  37. // between the genome and the target phrase.
  38. typedef struct {
  39. char locus[GENOME_LEN];
  40. double fitness;
  41. int matches;
  42. } genome_t;
  43.  
  44. genome_t target; // target phrase
  45. genome_t genome_array[POPULATION_SIZE]; // genomes of all members of the population
  46.  
  47. char weasel_to_ascii(char c) {
  48. switch(c) {
  49. case 0: return ' ';
  50. case 1: return 'A';
  51. case 2: return 'B';
  52. case 3: return 'C';
  53. case 4: return 'D';
  54. case 5: return 'E';
  55. case 6: return 'F';
  56. case 7: return 'G';
  57. case 8: return 'H';
  58. case 9: return 'I';
  59. case 10: return 'J';
  60. case 11: return 'K';
  61. case 12: return 'L';
  62. case 13: return 'M';
  63. case 14: return 'N';
  64. case 15: return 'O';
  65. case 16: return 'P';
  66. case 17: return 'Q';
  67. case 18: return 'R';
  68. case 19: return 'S';
  69. case 20: return 'T';
  70. case 21: return 'U';
  71. case 22: return 'V';
  72. case 23: return 'W';
  73. case 24: return 'X';
  74. case 25: return 'Y';
  75. case 26: return 'Z';
  76. }
  77. }
  78.  
  79. char ascii_to_weasel(char c) {
  80. switch(c) {
  81. case ' ': return 0;
  82. case 'A': return 1;
  83. case 'B': return 2;
  84. case 'C': return 3;
  85. case 'D': return 4;
  86. case 'E': return 5;
  87. case 'F': return 6;
  88. case 'G': return 7;
  89. case 'H': return 8;
  90. case 'I': return 9;
  91. case 'J': return 10;
  92. case 'K': return 11;
  93. case 'L': return 12;
  94. case 'M': return 13;
  95. case 'N': return 14;
  96. case 'O': return 15;
  97. case 'P': return 16;
  98. case 'Q': return 17;
  99. case 'R': return 18;
  100. case 'S': return 19;
  101. case 'T': return 20;
  102. case 'U': return 21;
  103. case 'V': return 22;
  104. case 'W': return 23;
  105. case 'X': return 24;
  106. case 'Y': return 25;
  107. case 'Z': return 26;
  108. }
  109. }
  110.  
  111. // Swap two genomes given their indices.
  112. void swap_genomes(int g1, int g2) {
  113. genome_t temp_genome;
  114. if (g1 == g2) {
  115. return;
  116. } else {
  117. temp_genome = genome_array[g1];
  118. genome_array[g1] = genome_array[g2];
  119. genome_array[g2] = temp_genome;
  120. }
  121. }
  122.  
  123. // Thoroughly and randomly scramble the population within the genome array.
  124. void scramble_genomes() {
  125. for (int i=0; i<6; i++) {
  126. for (int j=0; j<POPULATION_SIZE; j++) {
  127. swap_genomes(j, rand()%POPULATION_SIZE);
  128. }
  129. }
  130. }
  131.  
  132. // Calculate a genome's fitness by determining the number of loci at which
  133. // it matches the target, then applying the formula (1+s)^m where s is the selection
  134. // coefficient and m is the number of matches.
  135. double fitness(genome_t *genome) {
  136. genome->matches = 0;
  137.  
  138. // scan the entire genome, counting the number of loci that match the target
  139. for (int i=0; i < GENOME_LEN; i++) {
  140. if (genome->locus[i] == target.locus[i]) ++genome->matches;
  141. }
  142.  
  143. return pow(1.0+selection_coefficient, genome->matches);
  144. }
  145.  
  146. // Display a genome as a character string.
  147. void display_genome(genome_t *genome) {
  148. for (int i=0; i < GENOME_LEN; i++) {
  149. putchar(weasel_to_ascii(genome->locus[i]));
  150. }
  151. }
  152.  
  153. // Convert an ascii string to a Weasel genome
  154. void weaselize (char *ascii_str, genome_t *genome) {
  155. for (int i=0; i<GENOME_LEN; i++) {
  156. genome->locus[i] = ascii_to_weasel(ascii_str[i]);
  157. }
  158. }
  159.  
  160. // Move cursor to the home position
  161. void cursor_home() {
  162. printf("\x1b[H");
  163. }
  164.  
  165. // Overwrite everything on the screen with spaces
  166. void clear_screen() {
  167. cursor_home();
  168. for (int i=0; i < NUM_LINES; i++) {
  169. for (int j=0; j < CHARS_PER_LINE; j++) {
  170. putchar(' ');
  171. }
  172. printf("\n\r");
  173. }
  174. }
  175.  
  176. // Mutate 'old' genome, placing results in 'new'
  177. void mutate(genome_t *old, genome_t *new) {
  178. for (int i=0; i < GENOME_LEN; i++) {
  179. if ((double)rand()/(double)RAND_MAX < mutation_rate * 27.0/26.0 && (!ENABLE_LATCHING || old->locus[i] != target.locus[i])) {
  180. new->locus[i] = (char)(rand()%CHARSET_SIZE); // mutate this locus
  181. } else {
  182. new->locus[i] = old->locus[i]; // don't mutate
  183. }
  184. }
  185. new->fitness = fitness(new);
  186. }
  187.  
  188. // Initialize a genome with a random string
  189. void init_genome_rand(genome_t *genome) {
  190. for (int i=0; i < GENOME_LEN; i++) {
  191. genome->locus[i] = (char)(rand()%CHARSET_SIZE);
  192. }
  193. genome->fitness = fitness(genome);
  194. }
  195.  
  196. // Comparison function for use by qsort() library routine
  197. int compare_fitness (const void * elem1, const void * elem2) {
  198. if (((genome_t *)elem2)->fitness > ((genome_t *)elem1)->fitness) return 1;
  199. if (((genome_t *)elem2)->fitness == ((genome_t *)elem1)->fitness) return 0;
  200. return -1;
  201. }
  202.  
  203. // Use weighted random selection to choose offspring for the survivor slots
  204. void pick_survivors() {
  205.  
  206. double total_fitness = 0.0;
  207. double cumulative_fitness;
  208. double rand_num;
  209. int j;
  210.  
  211. // add up all fitnesses
  212. for (int i=0; i<POPULATION_SIZE; i++) {
  213. total_fitness += genome_array[i].fitness;
  214. }
  215.  
  216. // for each survivor slot
  217. for (int i=0; i<num_survivors; i++) {
  218.  
  219. cumulative_fitness = 0.0;
  220.  
  221. // generate a random number in the range 0-total_fitness
  222. rand_num = (double)rand()/(double)RAND_MAX * total_fitness;
  223.  
  224. // from the remaining offspring, select the one whose range is hit by the random number
  225. for (j=i; j<POPULATION_SIZE; j++) {
  226. cumulative_fitness += genome_array[j].fitness;
  227. if (cumulative_fitness >= rand_num) {
  228. break;
  229. }
  230. }
  231.  
  232. // swap it with the one in the survivor slot
  233. swap_genomes(i,j);
  234.  
  235. // adjust the total fitness to reflect only the remaining offspring
  236. total_fitness -= genome_array[i].fitness;
  237. }
  238. }
  239.  
  240. void display_histogram() {
  241. long long max = 0;
  242. long long width;
  243.  
  244. printf("Histogram of Hamming distances:\n\n\r");
  245.  
  246. for (int i=0; i<=GENOME_LEN; i++) {
  247. if (histogram[i] > max) {
  248. max = histogram[i];
  249. }
  250. }
  251.  
  252. if (max > 0) {
  253. for (int i=0; i<=GENOME_LEN; i++) {
  254. printf("%2d: %8lld ", i, histogram[i]);
  255. width = 60 * histogram[i]/max;
  256. for (int j=0; j<width; j++) {
  257. printf("X");
  258. }
  259. printf("\n\r");
  260. }
  261. }
  262. printf("\n\r");
  263. }
  264.  
  265. //
  266. // From http://stackoverflow.com/questions/448944/c-non-blocking-keyboard-input
  267. //
  268.  
  269. struct termios orig_termios;
  270.  
  271. void reset_terminal_mode()
  272. {
  273. tcsetattr(0, TCSANOW, &orig_termios);
  274. }
  275.  
  276. void set_conio_terminal_mode()
  277. {
  278. struct termios new_termios;
  279.  
  280. /* take two copies - one for now, one for later */
  281. tcgetattr(0, &orig_termios);
  282. memcpy(&new_termios, &orig_termios, sizeof(new_termios));
  283.  
  284. /* register cleanup handler, and set the new terminal mode */
  285. atexit(reset_terminal_mode);
  286. cfmakeraw(&new_termios);
  287. tcsetattr(0, TCSANOW, &new_termios);
  288. }
  289.  
  290. int kbhit()
  291. {
  292. struct timeval tv = { 0L, 0L };
  293. fd_set fds;
  294. FD_ZERO(&fds);
  295. FD_SET(0, &fds);
  296. return select(1, &fds, NULL, NULL, &tv);
  297. }
  298.  
  299. int getch()
  300. {
  301. int r;
  302. unsigned char c;
  303. if ((r = read(0, &c, sizeof(c))) < 0) {
  304. return r;
  305. } else {
  306. return c;
  307. }
  308. }
  309.  
  310. // Read a double-precision floating point number from keyboard input.
  311. // Can't use scanf here because of the non-blocking I/O.
  312. void get_double(double *ptr) {
  313. int i = 0;
  314. char c;
  315. do {
  316. while (!kbhit()) { // wait for keystroke
  317. ;
  318. }
  319. c = getch();
  320. putchar(c);
  321. fflush(stdout);
  322. buffer[i++] = c;
  323. } while (buffer[i-1] != '\r'); // accumulate characters in buffer until CR
  324. buffer[i-1] = '\n';
  325. i=0;
  326. fflush(stdout);
  327. sscanf(buffer, "%lf", ptr);
  328. }
  329.  
  330. int main() {
  331.  
  332. int i;
  333. int selection_enabled = ENABLE_SELECTION;
  334. char ascii_target[GENOME_LEN+1];
  335. char c;
  336.  
  337. set_conio_terminal_mode();
  338.  
  339. printf("\n\n\n\n\r");
  340.  
  341. // set cursor to home position, clear screen, and rehome
  342. clear_screen();
  343. cursor_home();
  344.  
  345. // seed the random number generator with the current time
  346. srand(time(NULL));
  347.  
  348. // set up ascii version of initial fitness target
  349. ascii_target[0] = 'M';
  350. ascii_target[1] = 'E';
  351. ascii_target[2] = 'T';
  352. ascii_target[3] = 'H';
  353. ascii_target[4] = 'I';
  354. ascii_target[5] = 'N';
  355. ascii_target[6] = 'K';
  356. ascii_target[7] = 'S';
  357. ascii_target[8] = ' ';
  358. ascii_target[9] = 'I';
  359. ascii_target[10] = 'T';
  360. ascii_target[11] = ' ';
  361. ascii_target[12] = 'I';
  362. ascii_target[13] = 'S';
  363. ascii_target[14] = ' ';
  364. ascii_target[15] = 'L';
  365. ascii_target[16] = 'I';
  366. ascii_target[17] = 'K';
  367. ascii_target[18] = 'E';
  368. ascii_target[19] = ' ';
  369. ascii_target[20] = 'A';
  370. ascii_target[21] = ' ';
  371. ascii_target[22] = 'W';
  372. ascii_target[23] = 'E';
  373. ascii_target[24] = 'A';
  374. ascii_target[25] = 'S';
  375. ascii_target[26] = 'E';
  376. ascii_target[27] = 'L';
  377.  
  378. // set up weasel version of the fitness target
  379. weaselize(ascii_target, &target);
  380.  
  381. // randomly initialize the genomes of the entire population
  382. for (i=0; i < POPULATION_SIZE; i++) {
  383. init_genome_rand(&genome_array[i]);
  384. }
  385. printf("\n\n\rInitial Organism 0 Genome:\n\n\r");
  386. display_genome(&genome_array[0]);
  387. printf("\n\n\n\r");
  388.  
  389. generation = 0;
  390. mutation_rate = MUTATION_RATE;
  391. selection_coefficient = SELECTION_COEFF;
  392. num_survivors = NUM_SURVIVORS;
  393.  
  394. // initialize the histogram to zeroes
  395. for (int i=0; i<=GENOME_LEN; i++) {
  396. histogram[i] = 0;
  397. }
  398.  
  399. while (1) {
  400.  
  401. // preserve the survivors, but convert the rest into mutated copies of the survivors
  402. for (i=0; i < POPULATION_SIZE - num_survivors; i++) {
  403. mutate(&genome_array[i%num_survivors], &genome_array[i+num_survivors]);
  404. }
  405.  
  406. // now mutate the survivors themselves
  407. for (i=0; i<num_survivors; i++) {
  408. mutate(&genome_array[i], &genome_array[i]);
  409. }
  410.  
  411. if (selection_enabled) {
  412. // use weighted random selection to choose offspring for the survivor slots
  413. pick_survivors();
  414. } else {
  415. // randomly scramble the genome array
  416. scramble_genomes();
  417. }
  418.  
  419. ++histogram[GENOME_LEN-genome_array[0].matches];
  420.  
  421. ++generation;
  422.  
  423. // in step mode, pause and display all genomes periodically
  424. if (STEP_MODE && generation % DISPLAY_INTERVAL == 0) {
  425. clear_screen();
  426. cursor_home();
  427. printf("\n\rTarget Phrase: ");
  428. printf("%s", ascii_target);
  429. printf("\n\n\rGeneration: %lld Number of survivors per generation: %d\n\r", generation, num_survivors);
  430. printf("\n\rSelection: %s Coefficient: %1.2lf Mutation rate: %1.2lf\n\n\r", selection_enabled ? "ON" : "OFF", selection_coefficient, mutation_rate);
  431. for (i = 0; i < GENOMES_TO_DISPLAY; i++) {
  432. printf("Organism %d Fitness %1.2le Hamming distance %d\n\n\r", i, genome_array[i].fitness, GENOME_LEN-genome_array[0].matches);
  433. display_genome(&genome_array[i]);
  434. printf("\r\n\n");
  435. }
  436. display_histogram();
  437. usleep(PAUSE_TIME); // pause briefly before continuing
  438. }
  439.  
  440. if (!STEP_MODE && generation % DISPLAY_INTERVAL == 0) {
  441. printf("generation = %lld fitness = %1.2le Hamming distance = %d\n\r", generation, genome_array[0].fitness, GENOME_LEN-genome_array[0].matches);
  442. }
  443.  
  444. if (kbhit()) { // keystroke detected
  445. switch(getch()) {
  446.  
  447. case 'c': // clear the histogram
  448. for (int i=0; i<=GENOME_LEN; i++) {
  449. histogram[i] = 0;
  450. }
  451. break;
  452.  
  453. case 'f': // change the selection coefficient
  454. printf("Enter new selection coefficient: ");
  455. fflush(stdout);
  456. get_double(&selection_coefficient);
  457. break;
  458.  
  459. case 'h': // print help message
  460. printf("Commands:\r\n");
  461. printf(" c - clear the histogram data\r\n");
  462. printf(" f - change the selection coefficient\r\n");
  463. printf(" h - print this help message\r\n");
  464. printf(" m - change the mutation rate\r\n");
  465. printf(" p - pause until a key is pressed\r\n");
  466. printf(" q - quit the program\r\n");
  467. printf(" s - toggle selection on/off\r\n");
  468. printf(" t - change the target phrase\r\n");
  469. printf(" v - change the number of survivors per generation\r\n");
  470. printf("\r\nHit any key to continue\r\n");
  471. while(!kbhit()){
  472. ;
  473. }
  474. (void)getch(); // consume character
  475. break;
  476.  
  477. case 'm': // change the mutation rate
  478. printf("Enter new mutation rate: ");
  479. fflush(stdout);
  480. get_double(&mutation_rate);
  481. break;
  482.  
  483. case 'p': // pause until next keystroke
  484. printf("Paused: Hit any key to continue\r\n");
  485. while(!kbhit()){
  486. ;
  487. }
  488. (void)getch(); // consume character
  489. break;
  490.  
  491. case 'q': // quit the program
  492. exit(0);
  493.  
  494. case 's': // toggle selection on and off
  495. selection_enabled = !selection_enabled;
  496. break;
  497.  
  498. case 't': // set new target phrase
  499. printf("Enter new target phrase (spaces and letters only): ");
  500. fflush(stdout);
  501. i = 0;
  502. do {
  503. while (!kbhit()) { // wait for keystroke
  504. ;
  505. }
  506. c = toupper(getch());
  507. if (c == '\r') break;
  508. if (isalpha(c) || c == ' ') {
  509. putchar(c);
  510. fflush(stdout);
  511. ascii_target[i++] = c; // add character to target phrase
  512. }
  513. } while (ascii_target[i-1] != '\r' && i-1 < GENOME_LEN);
  514. for(; i<GENOME_LEN; i++) {
  515. ascii_target[i] = ' '; // pad remainder of target with spaces
  516. }
  517. weaselize(ascii_target, &target);
  518. // need to recompute fitness values because the target has changed
  519. for (i=0; i<POPULATION_SIZE; i++) {
  520. genome_array[i].fitness = fitness(&genome_array[i]);
  521. }
  522. break;
  523.  
  524. case 'v': // change the number of survivors per generation
  525. printf("Enter desired number of survivors: ");
  526. fflush(stdout);
  527. get_double(&floating_num_survivors);
  528. num_survivors = (int)floating_num_survivors;
  529. break;
  530.  
  531. }
  532. }
  533. }
  534.  
  535. printf("\n\n\rFinal generation = %lld fitness = %1.2le Hamming distance = %d\n\r", generation, genome_array[0].fitness, GENOME_LEN-genome_array[0].fitness);
  536. printf("\n\rFinal genome:\n\n\r");
  537. display_genome(&genome_array[0]);
  538. printf("\n\n\r");
  539. tcsetattr(0, TCSANOW, &orig_termios);
  540. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement