/* MPIportfolio.c : apply the parallelized genetic algorithm to the portfolio problem, which finds an optimal solution to invest in stocks. @Author: Hyun Kim @Date: 05/04/00 */ #include #include #include #include #include "mpi.h" /* declaration of functions */ #define POPSIZE 60 /* population size */ #define GENES 12 /* number of genes per chromosome */ #define MAXGEN 150 enum Selection { roulette_wheel_selection, tournament_with_replacement }; enum Crossover { ptx_1, ptx_2, uniform, linx, alinx_1, alinx_2, alinx_3, alinx_4 }; struct Chromosome { double genes[GENES]; double fitness; }; struct Population { struct Chromosome chromosomes[POPSIZE]; struct Chromosome child[2]; double sumFitness, maxFitness, minFitness, avgFitness; int maxIndex, minIndex; }; /* collection of the parameters. */ struct Parameter { int selection; /* selection method used */ int crossover; /* crossover operator used */ double seed; /* seed for random number generator. */ double riskfree; /* risk free rate */ double expReturn[GENES]; /* expected return of each asset */ double stdDevReturn[GENES]; /* standard deviation of return */ double correlation[GENES][GENES]; /* correlation between a pair of assets */ double mutationRate; /* fixed as 1/(# of genes) */ }; struct Parameter param = { roulette_wheel_selection, linx, 0.09994, /* riskfree */ 0.04, /* exp return */ 0.29, 0.19, 0.29, 0.35, 0.14, 0.21, 0.26, 0.14, 0.15, 0.09, 0.11, 0.08, /* std dev of return */ 0.03, 0.02, 0.04, 0.06, 0.02, 0.04, 0.06, 0.03, 0.05, 0.02, 0.04, 0.03, /* correlation */ 0.04, 0.035, 0.06, 0.09, 0.03, 0.06, 0.09, 0.045, 0.075, 0.03, 0.06, 0.045, 0.035, 0.05, 0.04, 0.06, 0.02, 0.04, 0.06, 0.03, 0.05, 0.02, 0.04, 0.03, 0.06, 0.04, 0.0525, 0.12, 0.04, 0.08, 0.12, 0.06, 0.10, 0.04, 0.08, 0.06, 0.09, 0.06, 0.12, 0.052, 0.06, 0.12, 0.18, 0.09, 0.15, 0.06, 0.12, 0.09, 0.03, 0.02, 0.04, 0.06, 0.058, 0.04, 0.06, 0.03, 0.05, 0.02, 0.04, 0.03, 0.06, 0.04, 0.08, 0.12, 0.04, 0.0493, 0.12, 0.06, 0.1, 0.04, 0.08, 0.06, 0.09, 0.06, 0.12, 0.18, 0.06, 0.12, 0.0475, 0.09, 0.15, 0.06, 0.12, 0.09, 0.045, 0.03, 0.06, 0.09, 0.03, 0.06, 0.09, 0.0456, 0.075, 0.03, 0.06, 0.045, 0.075, 0.05, 0.10, 0.15, 0.05, 0.1, 0.15, 0.075, 0.043, 0.05, 0.1, 0.075, 0.03, 0.02, 0.04, 0.06, 0.02, 0.04, 0.06, 0.03, 0.05, 0.0409, 0.04, 0.03, 0.06, 0.04, 0.08, 0.12, 0.04, 0.08, 0.12, 0.06, 0.1, 0.04, 0.0388, 0.06, 0.045, 0.03, 0.06, 0.09, 0.03, 0.06, 0.09, 0.045, 0.075, 0.03, 0.06, 0.0365 }; void swap( int* d1, int* d2 ); void swapD( double* d1, double* d2 ); double sqr( double d ); void randomize( double seed ); /* initializes the random number generator */ double random01(); /* returns a random number between 0 and 1 */ struct Chromosome LINXchild( struct Chromosome* parent1, struct Chromosome* parent2 ); void doPTX1( struct Population* pop, struct Chromosome* parent1, struct Chromosome* parent2 ); void doUNIFORM( struct Population* pop, struct Chromosome* parent1, struct Chromosome* parent2 ); void doLINX( struct Population* pop, struct Chromosome* parent1, struct Chromosome* parent2 ); double objectiveFunction( struct Chromosome* chrom ); void sort( struct Population* pop, int* arr, int size ); void crossover( struct Population* pop, struct Chromosome* parent1, struct Chromosome* parent2 ); /* produce children using a crossove operator */ void statistics( struct Population* pop ); /* calculate the total/max/min/avg of the population fitnesses */ /* implementation of functions */ void random( struct Chromosome* chrom ) { /* meets the first constraint, i.e. sigma(Xi) = 1 where i = 1 to N To ensure the sum of the N weights equals 1 and all N weights are positive numbers between 0 to 1, the following procedure is followed: 1. Generate N random numbers. 2. Assign the i-th random number divided by the sum of the N random numbers as the weight for i-th stock. */ double total = 0; int i; for( i = 0; i < GENES; i++ ) { chrom->genes[i] = random01(); total += chrom->genes[i]; } for( i = 0; i < GENES; i++ ) chrom->genes[i] /= total; } void evaluate( struct Chromosome* chrom ) { chrom->fitness = objectiveFunction( chrom ); } int getRandomPoint() { return rnd( 0, GENES - 1 ); } void transpose( struct Chromosome* chrom ) { int p1, p2; if( random01() > param.mutationRate ) return; /* transpose randomly chosen two genes within the mutation rate */ p1 = getRandomPoint( chrom ); p2 = getRandomPoint( chrom ); if( p1 != p2 ) swapD( &chrom->genes[p1], &chrom->genes[p2] ); } void mutate( struct Chromosome* chrom ) { int p; if( random01() > param.mutationRate ) return; /* mutate randomly chosen gene within the mutation rate */ p = getRandomPoint(); chrom->genes[p] = 1 - chrom->genes[p]; /*adjust( param.genes ); */ } void adjust( struct Chromosome* chrom, int n ) { /* constraint 1: all values of each gene should be one. constraint 2: the value of the gene should be positive. */ int i; double total = 0; for( i = 0; i < GENES; i++ ) total += chrom->genes[i]; for( i = 0; i < GENES; i++ ) chrom->genes[i] /= total; } double objectiveFunction( struct Chromosome* chrom ) { /* Sharpe Ratio is used as an objective function: SharpeRatio = ( ExpectedReturn - RiskFreeRate ) / StandardDeviation For performance, parallelization is applied. Each processor computes its own portion of summation and pass the result to the processor 0. */ double portfolioExpReturn, variance, myfitness; int i, j, my_rank, num_processors, tag = 0, dest = 0; double local_data[2] = { 0.0, 0.0 }, /* 1st data: expected return, 2nd data: variance */ covariance = 0; MPI_Status status; MPI_Comm_rank( MPI_COMM_WORLD, &my_rank ); /* get process rank */ MPI_Comm_size( MPI_COMM_WORLD, &num_processors ); /* get number of processors */ for( i = my_rank*GENES/num_processors; i < (my_rank+1)*GENES/num_processors; i++ ) { local_data[0] += param.expReturn[i] * chrom->genes[i]; local_data[1] += sqr( chrom->genes[i] ) * sqr( param.stdDevReturn[i] ); for( j = 0; j < GENES; j++ ) { if( i != j ) { covariance = param.correlation[i][j] * param.stdDevReturn[i] * param.stdDevReturn[j]; local_data[1] += chrom->genes[i] * chrom->genes[j] * covariance; } } } if( my_rank == 0 ) { portfolioExpReturn = local_data[0]; variance = local_data[1]; for( i = 1; i < num_processors; i++ ) { MPI_Recv( &local_data, 2, MPI_DOUBLE, i, tag, MPI_COMM_WORLD, &status ); portfolioExpReturn += local_data[0]; variance += local_data[1]; } } else { MPI_Send( &local_data, 2, MPI_DOUBLE, dest, tag, MPI_COMM_WORLD ); } myfitness = ( portfolioExpReturn - param.riskfree ) / sqrt( variance ); return myfitness; } void printChromosome( struct Chromosome* chrom ) { int i; for( i = 0; i < GENES; i++ ) printf( "%.7f ", chrom->genes[i] ); } void fprintChromosome( FILE* f, struct Chromosome* chrom ) { int i; for( i = 0; i < GENES; i++ ) fprintf( f, "%.7f ", chrom->genes[i] ); } double getExpReturn( struct Chromosome* chrom ) { double portfolioExpReturn = 0; int i; for( i = 0; i < GENES; i++ ) portfolioExpReturn += param.expReturn[i] * chrom->genes[i]; return portfolioExpReturn; } double getStdDev( struct Chromosome* chrom ) { double variance = 0, covariance = 0; int i, j; for( i = 0; i < GENES; i++ ) { variance += sqr( chrom->genes[i] ) * sqr( param.stdDevReturn[i] ); for( j = i + 1; j < GENES; j++ ) { covariance = param.correlation[i][j] * param.stdDevReturn[i] * param.stdDevReturn[j]; variance += 2 * chrom->genes[i] * chrom->genes[j] * covariance; } } return sqrt( variance ); } struct Population reproduce( struct Population* pop ) { int i = 0; /* number of generations */ struct Population newPop; int p1, p2; while( i < POPSIZE ) { p1 = selection( pop ); p2 = selection( pop ); if( p1 != p2 ) { /* produce offsprings */ crossover( pop, &(pop->chromosomes[p1]), &(pop->chromosomes[p2]) ); newPop.chromosomes[i++] = pop->child[0]; newPop.chromosomes[i++] = pop->child[1]; statistics( &newPop ); } else newPop = reproduce( pop ); } return newPop; } struct Population reproduceWithReplacement( struct Population* pop ) { struct Population newPop; int p1 = selection( pop ); int p2 = selection( pop ); if( p1 != p2 ) { int minIndex1, minIndex2; /* produce offsprings */ crossover( pop, &(pop->chromosomes[p1]), &(pop->chromosomes[p2]) ); newPop = *pop; minIndex1 = pop->minIndex; minIndex2 = getIndexAt( pop, POPSIZE - 1 ); newPop.chromosomes[minIndex1] = pop->child[0]; newPop.chromosomes[minIndex2] = pop->child[1]; statistics( &newPop ); } else newPop = reproduceWithReplacement( pop ); return newPop; } void statistics( struct Population* pop ) { /* initialize */ int i = 0; pop->sumFitness = pop->chromosomes[i].fitness; pop->maxFitness = pop->chromosomes[i].fitness; pop->minFitness = pop->chromosomes[i].fitness; pop->avgFitness = pop->chromosomes[i].fitness; pop->maxIndex = i; pop->minIndex = i; /* find the total/max/avg/min fitness */ for( i = 1; i < POPSIZE; i++ ) { double newFitness = pop->chromosomes[i].fitness; pop->sumFitness += newFitness; if( newFitness > pop->maxFitness ) { pop->maxFitness = newFitness; pop->maxIndex = i; } else if( newFitness < pop->minFitness ) { pop->minFitness = newFitness; pop->minIndex = i; } } pop->avgFitness = pop->sumFitness / POPSIZE; } void initialize( struct Population* pop ) { int i; randomize( param.seed ); for( i = 0; i < POPSIZE; i++ ) { random( &(pop->chromosomes[i]) ); evaluate( &(pop->chromosomes[i]) ); } statistics( pop ); } int selection( struct Population* pop ) { int i = -1; /* chosen chromosome */ switch( param.selection ) { case roulette_wheel_selection: i = rouletteWheelSelection( pop ); break; case tournament_with_replacement: break; default: break; } return i; } void crossover( struct Population* pop, struct Chromosome* parent1, struct Chromosome* parent2 ) { switch( param.crossover ) { case ptx_1: doPTX1( pop, parent1, parent2 ); break; case ptx_2: break; case uniform: doUNIFORM( pop, parent1, parent2 ); break; case linx: doLINX( pop, parent1, parent2 ); break; case alinx_1: break; case alinx_2: break; case alinx_3: break; case alinx_4: break; default: break; } /* child1.mutate(); // mutation does not include adjusting function child2.mutate(); */ adjust( &(pop->child[0]), GENES ); adjust( &(pop->child[1]), GENES ); /* after adjusting, apply transpose */ transpose( &(pop->child[0]) ); transpose( &(pop->child[1]) ); evaluate( &(pop->child[0]) ); evaluate( &(pop->child[1]) ); } void printPopulation( struct Population* pop ) { int i; for( i = 0; i < POPSIZE; i++ ) printChromosome( &(pop->chromosomes[ i ]) ); } void fprintPopulation( FILE* f, struct Population* pop ) { int i; for( i = 0; i < POPSIZE; i++ ) fprintChromosome( f, &(pop->chromosomes[i]) ); } int rouletteWheelSelection( struct Population* pop ) { double rand = pop->sumFitness * random01(); /* random point on wheel */ double partialSum = 0; int i = -1; while( partialSum < rand && i < POPSIZE ) { i++; partialSum += pop->chromosomes[i].fitness; } return i; } void doPTX1( struct Population* pop, struct Chromosome* parent1, struct Chromosome* parent2 ) { int length = GENES; int xPoint = rnd( 0, length-1 ); /* crossover point */ int i; for( i = 0; i < xPoint; i++ ) { pop->child[0].genes[i] = parent1->genes[i]; pop->child[1].genes[i] = parent2->genes[i]; } for( i = xPoint; i < length; i++ ) { pop->child[0].genes[i] = parent2->genes[i]; pop->child[1].genes[i] = parent1->genes[i]; } } void doUNIFORM( struct Population* pop, struct Chromosome* parent1, struct Chromosome* parent2 ) { double r; int i; for( i = 0; i < POPSIZE; i++ ) { r = random01(); if( r < 0.5 ) { pop->child[0].genes[i] = parent1->genes[i]; pop->child[1].genes[i] = parent2->genes[i]; } else { pop->child[0].genes[i] = parent2->genes[i]; pop->child[1].genes[i] = parent1->genes[i]; } } } void doLINX( struct Population* pop, struct Chromosome* parent1, struct Chromosome* parent2 ) { pop->child[0] = LINXchild( parent1, parent2 ); pop->child[1] = LINXchild( parent1, parent2 ); } int getIndexAt( struct Population* pop, int n ) { /* return the index which indicates the chromosome with the N-th largest fitness */ int sorter[POPSIZE], i; for( i = 0; i < POPSIZE; i++ ) sorter[i] = i; sort( pop, sorter, POPSIZE ); return sorter[n-1]; } struct Chromosome LINXchild( struct Chromosome* parent1, struct Chromosome* parent2 ) { struct Chromosome child; int length = GENES; /* s1(or s2) is the set of offspring's genes inherited so far from parent1(or parent2) */ int s1[GENES]; int s2[GENES]; int cntS1 = 0, cntS2 = 0; /* number of s1(or s2) */ int i, j, visited[GENES]; double linkage[GENES][GENES]; int point1, point2; /* compute linkage_matrix Note: linkage_matrix = (1 - correlation_matrix) / 2, since -1 <= correlation_matrix <= 1. */ for( i = 0; i < GENES; i++ ) { visited[i] = 0; /* initialize the visited array */ for( j = 0; j < GENES; j++ ) linkage[i][j] = (1 - param.correlation[i][j])/2; } /* For two randomly chosen genes, select one allele from parent1 and the other from parent2 */ point1 = rnd( 0, length - 1 ); point2 = rnd( 0, length - 1 ); if( point1 == point2 ) point2 = ( point2 + 1 ) % length; child.genes[point1] = parent1->genes[point1]; s1[cntS1++] = point1; child.genes[point2] = parent2->genes[point2]; s2[cntS2++] = point2; visited[point2] = visited[point2] = 1; /* copy all common genes from parents to offspring */ for( i = 0; i < length; i++ ) { if( parent1->genes[i] == parent2->genes[i] ) { child.genes[i] = parent1->genes[i]; visited[i] = 1; } } for( i = 0; i < length; i++ ) { double h1 = 1, h2 = 1; double r; if( visited[i] ) continue; /* compute h1 and h2 */ for( j = 0; j < cntS1; j++ ) { h1 = h1 * linkage[ s1[j] ][ i ]; h2 = h2 * ( 1 - linkage[ s1[j] ][ i ] ); } for( j = 0; j < cntS2; j++ ) { h1 = h1 * ( 1 - linkage[ s2[j] ][ i ] ); h2 = h2 * linkage[ s2[j] ][ i ]; } r = random01(); /* generate any random number in [0, 1] */ if( r <= h1 / ( h1 + h2 ) ) { child.genes[i] = parent1->genes[i]; s1[cntS1++] = i; } else { child.genes[i] = parent2->genes[i]; s2[cntS2++] = i; } visited[i] = 1; } return child; } void sort( struct Population* pop, int arr[], int size ) { /* bubble sort: arr contains indices of all chromosomes in Population */ int end = size - 1; int i; for( end = size-1; end >= 1; end-- ) for( i = 0; i <= end-1; i++ ) if( pop->chromosomes[arr[i]].fitness > pop->chromosomes[arr[i+1]].fitness ) swap( &arr[i], &arr[i+1] ); } void report( FILE* f, int gen, struct Population* pop ) { int i; fprintf( f, "Generation : %d\n", gen ); fprintf( f, "Fitness (max, avg, min) : %.7f %.7f %.7f\n", pop->maxFitness, pop->avgFitness, pop->minFitness ); fprintf( f, "Population Matrix (with fitness): \n\n" ); for( i = 0; i < POPSIZE; i++ ) { fprintChromosome( f, &(pop->chromosomes[i]) ); fprintf( f, ": %.7f\n", pop->chromosomes[i].fitness ); } fprintf( f, "\n------------------------------------------------------------\n" ); } void analyze( FILE* f, int gen, struct Population* pop ) { fprintf( f, "%.7f\n", pop->maxFitness ); } void result( FILE* f, struct Population* pop ) { fprintf( f, "Expected Return : %.7f\n", getExpReturn(&(pop->chromosomes[pop->maxIndex])) ); fprintf( f, "Std. Dev. ( Risk ) : %.7f\n", getStdDev(&(pop->chromosomes[pop->maxIndex])) ); fprintf( f, "Max Fitness : %.7f\n", pop->maxFitness ); fprintf( f, "Soultion : \n" ); fprintChromosome( f, &(pop->chromosomes[pop->maxIndex]) ); fprintf( f, "\n------------------------------------------------------------\n" ); } /* void readParameters( FILE* in, struct Parameter* param ) { char s[ 80 ] = ""; while( strcmp( s, "BEGIN" ) != 0 ) getline( s, 80, in ); fscanf( in, "%s %f", s, param->riskfree ); fscanf( in, "%s %s", s, s ); param->selection = findSelectionOp( s ); fscanf( in, "%s %s", s, s ); param->crossover = findCrossoverOp( s ); fscanf( in, "%s %f", s, param->seed ); randomize( param->seed ); fscanf( in, "%s", s ); readExpReturn( in, param->expReturn ); fscanf( in, "%s", s ); readStdDevReturn( in, param->stdDevReturn ); fscanf( in, "%s", s ); readCorrelation( in, param->correlation ); param->mutationRate = 1/(double)GENES; }*/ void printParameters( FILE* out, struct Parameter param ) { int i, j; fprintf( out, "GA settings:\n" ); fprintf( out, "\n------------------------------------------------------------\n" ); fprintf( out, "population size : %d\n", POPSIZE ); fprintf( out, "number of genes : %d\n", GENES ); fprintf( out, "risk-free rate : %.3f\n", param.riskfree ); fprintf( out, "selection scheme : %d\n", param.selection ); fprintf( out, "crossover operator : %d\n", param.crossover ); fprintf( out, "mutation rate : %.6f\n", param.mutationRate ); fprintf( out, "random seed : %.5f\n", param.seed ); fprintf( out, "expected return : " ); for( i = 0; i < GENES; i++ ) fprintf( out, "%.6f ", param.expReturn[i] ); fprintf( out, "\nStd. Dev. of Return : " ); for( i = 0; i < GENES; i++ ) fprintf( out, "%.6f ", param.stdDevReturn[i] ); fprintf( out, "\ncorrelation : " ); for( i = 0; i < GENES; i++ ) for( j = i; j < GENES; j++ ) fprintf( out, "%.6f ", param.correlation[i][j] ); fprintf( out, "\n\n------------------------------------------------------------\n" ); } void readExpReturn( FILE* in, double expReturn[] ) { /* read expected returns from the file */ int i; for( i = 0; i < GENES; i++ ) fscanf( in, "%f", param.expReturn[i] ); } void readStdDevReturn( FILE* in, double stdDevReturn[] ) { /* read expected returns from the file */ int i; for( i = 0; i < GENES; i++ ) fscanf( in, "%f", param.stdDevReturn[i] ); } void readCorrelation( FILE* in, double correlation[][GENES] ) { /* read correlation from the file */ int i, j; for( i = 0; i < GENES; i++ ) { for( j = i; j < GENES; j++ ) { fscanf( in, "%f", param.correlation[i][j] ); param.correlation[j][i] = param.correlation[i][j]; } } } int findSelectionOp( char* s ) { if( !strcmp( s, "roulette_wheel_selection" ) ) return roulette_wheel_selection; else if( !strcmp( s, "tournament_with_replacement" ) ) return tournament_with_replacement; else /* default selection operator */ return roulette_wheel_selection; } int findCrossoverOp( char* s ) { if( !strcmp( s, "ptx_1" ) ) return ptx_1; else if( !strcmp( s, "ptx_2" ) ) return ptx_2; else if ( !strcmp( s, "uniform" ) ) return uniform; else if ( !strcmp( s, "linx" ) ) return linx; else if ( !strcmp( s, "alinx_1" ) ) return alinx_1; else if ( !strcmp( s, "alinx_2" ) ) return alinx_2; else if ( !strcmp( s, "alinx_3" ) ) return alinx_3; else if ( !strcmp( s, "alinx_4" ) ) return alinx_4; else /* default crossover operator */ return ptx_1; } /* read a line, return length */ int getline( char* line, int max, FILE* f ) { if( fgets( line, max, f ) == NULL ) return 0; else return strlen( line ); } void swap( int* d1, int* d2 ) { int tmp; tmp = *d1; *d1 = *d2; *d2 = tmp; } void swapD( double* d1, double* d2 ) { double tmp; tmp = *d1; *d1 = *d2; *d2 = tmp; } double sqr( double d ) { return d * d; } double oldrand[55]; /* array of 55 random numbers */ int jrand; /* current random */ /* forward declarations */ void warmup_random( const double seed ); void advance_random(); /* * Initialize random number generator */ void randomize( double seed ) { warmup_random( seed ); } /* * Get random off and runnin */ void warmup_random( const double seed ) { int i,j; double new_random, prev_random; oldrand[54] = seed; new_random = 1.0e-9; prev_random = seed; for( j=1; j<=54; j++ ) { i = 21*j % 54; oldrand[i] = new_random; new_random = prev_random - new_random; if (new_random < 0.0) new_random++; prev_random = oldrand[i]; } advance_random(); advance_random(); advance_random(); jrand = 0; } /* * Create next batch of 55 random numbers */ void advance_random() { int i; double new_random; for( i=0; i<24; i++ ) { new_random = oldrand[i] - oldrand[i+31]; if (new_random < 0.0) new_random++; oldrand[i] = new_random; } for( i=24; i<55; i++ ) { new_random = oldrand[i] - oldrand[i-24]; if (new_random < 0.0) new_random++; oldrand[i] = new_random; } } /* * Fetch a single random number between 0.0 and 1.0 - Subtractive Method * See Knuth, D. (1969), v. 2 for details */ double random01() { jrand++; if (jrand>=55) { jrand = 1; advance_random(); } return oldrand[jrand]; } /* * Flip a biased coin and returns 0 or 1 */ int flip( double probability ) { if (probability == 1.0) return 1; return random01() <= probability; } /* * Pick a random integer in [low..high] */ int rnd( int low, int high ) { int i; if (low >= high) i=low; else { i = (int) (random01() * (high-low+1) + low); if (i>high) i=high; } return i; } main( int argc, char** argv ) { int i, my_rank; struct Population tmppop; struct Population pop; /* open input/ouput files */ FILE *outfile, *detailfile, *analfile; MPI_Init( &argc, &argv ); /* start up MPI */ MPI_Comm_rank( MPI_COMM_WORLD, &my_rank ); /* get process rank */ param.mutationRate = 1/(double)GENES; /* set the mutation rate */ if( my_rank == 0 ) { outfile = fopen( "MPIoutput3-6.txt", "w" ); /* for writing the output */ detailfile = fopen( "MPIdetail3-6.txt", "w" ); /* for writing the detail info */ analfile = fopen( "MPIanal3-6.txt", "w" ); printParameters( outfile, param ); } /* applying the genetic method */ printf( "init pop..\n" ); initialize( &pop ); /* initialize the population */ printf( "reporting..\n" ); if( my_rank == 0 ) { report( detailfile, 0, &pop ); analyze( analfile, 0, &pop ); } for( i = 0; i < MAXGEN; i++ ) /* reproduce the population */ { tmppop = reproduceWithReplacement( &pop ); pop = tmppop; if( my_rank == 0 ) { report( detailfile, i + 1, &pop ); analyze( analfile, i + 1, &pop ); } } printf( "%d\n", pop.maxIndex ); if( my_rank == 0 ) { analyze( analfile, MAXGEN, &pop ); result( outfile, &pop ); } MPI_Finalize(); /* close MPI */ fclose( outfile ); fclose( detailfile ); fclose( analfile ); }