Created
September 2, 2019 11:15
-
-
Save vedraiyani/f99be52f7ec4009a3b992f980adb49f8 to your computer and use it in GitHub Desktop.
simple genetic algorithm.ipynb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"nbformat": 4, | |
"nbformat_minor": 0, | |
"metadata": { | |
"colab": { | |
"name": "simple genetic algorithm.ipynb", | |
"version": "0.3.2", | |
"provenance": [], | |
"include_colab_link": true | |
}, | |
"kernelspec": { | |
"name": "python3", | |
"display_name": "Python 3" | |
} | |
}, | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "view-in-github", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"<a href=\"https://colab.research.google.com/gist/vedraiyani/f99be52f7ec4009a3b992f980adb49f8/simple-genetic-algorithm.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "0DRjsgWLqYon", | |
"colab_type": "code", | |
"colab": {} | |
}, | |
"source": [ | |
"import random\n", | |
"import numpy as np\n", | |
"\n", | |
"def create_reference_solution(chromosome_length):\n", | |
"\n", | |
" number_of_ones = int(chromosome_length / 2)\n", | |
"\n", | |
" # Build an array with an equal mix of zero and ones\n", | |
" reference = np.zeros(chromosome_length)\n", | |
" reference[0: number_of_ones] = 1\n", | |
"\n", | |
" # Shuffle the array to mix the zeros and ones\n", | |
" np.random.shuffle(reference)\n", | |
" \n", | |
" return reference" | |
], | |
"execution_count": 0, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "pWMktUBSqaEr", | |
"colab_type": "code", | |
"colab": {} | |
}, | |
"source": [ | |
"def create_starting_population(individuals, chromosome_length):\n", | |
" # Set up an initial array of all zeros\n", | |
" population = np.zeros((individuals, chromosome_length))\n", | |
" # Loop through each row (individual)\n", | |
" for i in range(individuals):\n", | |
" # Choose a random number of ones to create\n", | |
" ones = random.randint(0, chromosome_length)\n", | |
" # Change the required number of zeros to ones\n", | |
" population[i, 0:ones] = 1\n", | |
" # Sfuffle row\n", | |
" np.random.shuffle(population[i])\n", | |
" \n", | |
" return population" | |
], | |
"execution_count": 0, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "jg2Z1HPKqlmy", | |
"colab_type": "code", | |
"colab": {} | |
}, | |
"source": [ | |
"def calculate_fitness(reference, population):\n", | |
" # Create an array of True/False compared to reference\n", | |
" identical_to_reference = population == reference\n", | |
" # Sum number of genes that are identical to the reference\n", | |
" fitness_scores = identical_to_reference.sum(axis=1)\n", | |
" \n", | |
" return fitness_scores" | |
], | |
"execution_count": 0, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "UuslHVGQqpwt", | |
"colab_type": "code", | |
"colab": {} | |
}, | |
"source": [ | |
"def select_individual_by_tournament(population, scores):\n", | |
" # Get population size\n", | |
" population_size = len(scores)\n", | |
" \n", | |
" # Pick individuals for tournament\n", | |
" fighter_1 = random.randint(0, population_size-1)\n", | |
" fighter_2 = random.randint(0, population_size-1)\n", | |
" \n", | |
" # Get fitness score for each\n", | |
" fighter_1_fitness = scores[fighter_1]\n", | |
" fighter_2_fitness = scores[fighter_2]\n", | |
" \n", | |
" # Identify undividual with highest fitness\n", | |
" # Fighter 1 will win if score are equal\n", | |
" if fighter_1_fitness >= fighter_2_fitness:\n", | |
" winner = fighter_1\n", | |
" else:\n", | |
" winner = fighter_2\n", | |
" \n", | |
" # Return the chromsome of the winner\n", | |
" return population[winner, :]" | |
], | |
"execution_count": 0, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "ibuoLGKEquKT", | |
"colab_type": "code", | |
"colab": {} | |
}, | |
"source": [ | |
"def breed_by_crossover(parent_1, parent_2):\n", | |
" # Get length of chromosome\n", | |
" chromosome_length = len(parent_1)\n", | |
" \n", | |
" # Pick crossover point, avoding ends of chromsome\n", | |
" crossover_point = random.randint(1,chromosome_length-1)\n", | |
" \n", | |
" # Create children. np.hstack joins two arrays\n", | |
" child_1 = np.hstack((parent_1[0:crossover_point],\n", | |
" parent_2[crossover_point:]))\n", | |
" \n", | |
" child_2 = np.hstack((parent_2[0:crossover_point],\n", | |
" parent_1[crossover_point:]))\n", | |
" \n", | |
" # Return children\n", | |
" return child_1, child_2" | |
], | |
"execution_count": 0, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "NjxukWrtqywC", | |
"colab_type": "code", | |
"colab": {} | |
}, | |
"source": [ | |
"def randomly_mutate_population(population, mutation_probability):\n", | |
" \n", | |
" # Apply random mutation\n", | |
" random_mutation_array = np.random.random(\n", | |
" size=(population.shape))\n", | |
" \n", | |
" random_mutation_boolean = \\\n", | |
" random_mutation_array <= mutation_probability\n", | |
"\n", | |
" population[random_mutation_boolean] = \\\n", | |
" np.logical_not(population[random_mutation_boolean])\n", | |
" \n", | |
" # Return mutation population\n", | |
" return population" | |
], | |
"execution_count": 0, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "0fyQSYQAqI3Z", | |
"colab_type": "code", | |
"colab": { | |
"base_uri": "https://localhost:8080/", | |
"height": 51 | |
}, | |
"outputId": "36aab8a3-97ff-4b4a-e0cf-a2fb2ebe63f6" | |
}, | |
"source": [ | |
"# Set general parameters\n", | |
"chromosome_length = 75\n", | |
"population_size = 500\n", | |
"maximum_generation = 200\n", | |
"best_score_progress = [] # Tracks progress\n", | |
"\n", | |
"# Create reference solution \n", | |
"# (this is used just to illustrate GAs)\n", | |
"reference = create_reference_solution(chromosome_length)\n", | |
"\n", | |
"# Create starting population\n", | |
"population = create_starting_population(population_size, chromosome_length)\n", | |
"\n", | |
"# Display best score in starting population\n", | |
"scores = calculate_fitness(reference, population)\n", | |
"best_score = np.max(scores)/chromosome_length * 100\n", | |
"print ('Starting best score, percent target: %.1f' %best_score)\n", | |
"\n", | |
"# Add starting best score to progress tracker\n", | |
"best_score_progress.append(best_score)\n", | |
"\n", | |
"# Now we'll go through the generations of genetic algorithm\n", | |
"for generation in range(maximum_generation):\n", | |
" # Create an empty list for new population\n", | |
" new_population = []\n", | |
" \n", | |
" # Create new popualtion generating two children at a time\n", | |
" for i in range(int(population_size/2)):\n", | |
" parent_1 = select_individual_by_tournament(population, scores)\n", | |
" parent_2 = select_individual_by_tournament(population, scores)\n", | |
" child_1, child_2 = breed_by_crossover(parent_1, parent_2)\n", | |
" new_population.append(child_1)\n", | |
" new_population.append(child_2)\n", | |
" \n", | |
" # Replace the old population with the new one\n", | |
" population = np.array(new_population)\n", | |
" \n", | |
" # Score best solution, and add to tracker\n", | |
" scores = calculate_fitness(reference, population)\n", | |
" best_score = np.max(scores)/chromosome_length * 100\n", | |
" best_score_progress.append(best_score)\n", | |
"\n", | |
"# GA has completed required generation\n", | |
"print ('End best score, percent target: %.1f' %best_score)" | |
], | |
"execution_count": 11, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": [ | |
"Starting best score, percent target: 65.3\n", | |
"End best score, percent target: 100.0\n" | |
], | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "F1pBD7M8qPVX", | |
"colab_type": "code", | |
"colab": { | |
"base_uri": "https://localhost:8080/", | |
"height": 283 | |
}, | |
"outputId": "66efb063-7a44-4465-baa3-8f1af54bfe2b" | |
}, | |
"source": [ | |
"# Plot progress\n", | |
"import matplotlib.pyplot as plt\n", | |
"%matplotlib inline\n", | |
"plt.plot(best_score_progress)\n", | |
"plt.xlabel('Generation')\n", | |
"plt.ylabel('Best score (% target)')\n", | |
"plt.show()" | |
], | |
"execution_count": 12, | |
"outputs": [ | |
{ | |
"output_type": "display_data", | |
"data": { | |
"image/png": "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\nZHgAAAAASUVORK5CYII=\n", | |
"text/plain": [ | |
"<Figure size 432x288 with 1 Axes>" | |
] | |
}, | |
"metadata": { | |
"tags": [] | |
} | |
} | |
] | |
} | |
] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment