├── .gitignore ├── LICENSE ├── README.md ├── Week 1 - Intro to Python and Numpy ├── Numpy.ipynb └── Python.ipynb ├── Week 2 - Genetic Algorithm(part 1) ├── Genetic Operators.ipynb └── figs │ ├── chromosome.png │ ├── genetic_architecture.PNG │ ├── knapsack_problem.png │ ├── roulette_wheel.png │ └── tournament_selection.jpg ├── Week 3 - Genetic Algorithm(part 2) ├── Genetic Algorithm.ipynb ├── None0000000.png ├── figs │ ├── genetic_architecture.PNG │ ├── one_point_crossover.png │ ├── two_point_crossover.png │ ├── two_point_crossover_v1.jpg │ ├── two_point_crossover_v2.png │ └── uniform-crossover.png └── genetic_utils.py ├── Week 4 - Genetic Algorithm(part 3).rar ├── Week 4 - Genetic Algorithm(part 3) ├── Genetic Algorithm.ipynb ├── None0000000.png ├── figs │ └── gray_to_bin.PNG └── genetic_utils.py ├── Week 5 - Ant Colony Optimization ├── Ant Colony.ipynb ├── data │ ├── att48.txt │ ├── chn144.txt │ └── chn31.txt └── figs │ ├── aco_algorithm.PNG │ ├── strategy.PNG │ ├── transition_prob.PNG │ └── tsp.png ├── Week 6 - Particle Swarm Optimization ├── None0000000.png ├── PSO.ipynb └── pso_utils.py └── Week 7 - Logistic Regression ├── Logistic Regression.ipynb ├── data └── Iris.csv ├── figs └── logistic_regression.png └── utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Computational Intelligence Tutorial 2 | In this repository, we will cover the material of the Computational Intelligence course which originated in University of Guilan. This repo will demonstrate how you could implement Metaheuristic Algorithms in Python. Therefore, if you are interested in learning the implementation of Metaheuristic Algorithms and understanding them with cool visualization techniques, this is the right place for you. (The goal of making this content was enabling others to learn Metaheuristic Algorithms on their own; therefore, if you can't be present in the class, you can learn all of the material using this repository.) 3 | 4 | # Prerequisites 5 | Before you start this tutorial, you should know: 6 | * Python - If you don't know python, but you know how to program, you can learn it in first week of this class. 7 | * Numpy Library - If you don't know Numpy, you can learn it in first week of this class. 8 | * Algorithms & Datastructures - If you don't know Algorithms & Datastructures, you can learn it here. 9 | --- 10 | # Weekly Content 11 | ## First Week 12 | * Python 13 | * Basic Data Types 14 | * Containers 15 | * Functions 16 | * Classes 17 | * Numpy 18 | * Creating Arrays 19 | * Array Data 20 | * Reshaping an Array 21 | * Arithmetic Operations on Arrays 22 | * Conditional Operators 23 | * Mathematical and Statistical Functions 24 | * Array Indexing 25 | * Iterating 26 | * Saving and loading 27 | --- 28 | ## Second Week 29 | First three steps of Implementing Genetic Algorithms : 30 | * Population Initialization 31 | * Fitness Calculation 32 | * Selection Operators 33 | * Introduction to Knapsack problem 34 | * How you can create a fake data for Knapsack problem 35 | --- 36 | ## Third Week 37 | * Last two steps of implementing the Genetic Algorithms : 38 | * Crossover 39 | * Mutation 40 | * Solving Knapsack problem with Brute Force Algorithm 41 | * Solving Knapsack problem with Genetic Algorithm 42 | * Visualization of Genetic Algorithm output 43 | --- 44 | ## Fourth Week 45 | * How to find the minimum point of various functions 46 | * How to represent a floating-point chromosome as a binary chromosome 47 | * Uniform Mutation (Floating-point representation) 48 | * Linear Recombination Crossover 49 | * Genetic Algorithm (Floating-point Representation) 50 | * 3-D Visualization of every function & its fitness 51 | --- 52 | ## Fifth Week 53 | * Full ACO algorithm to find sub-optimal solutions for Travelling Sales Man (TSP) Problem 54 | * Implementing Three strategies for ACO algorithm including Ant-cycle AS, Ant-density AS, and Ant-quantity AS 55 | * Designing the visualization of the final route which ACO discovered 56 | --- 57 | ## Sixth Week 58 | * Full PSO algorithm to find the minimum point of Rastrigin Function 59 | * 3-D Visualization of sequential progress of PSO algorithm 60 | * 2-D Visualization of sequential progress of PSO algorithm 61 | --- 62 | ## Seventh Week 63 | * A detailed description of Binary Logistic Regression 64 | * Full clean implementation of Logistic Regression to discriminate Iris-setosa flower from other types of flowers 65 | * Visualization of flowers features and the discriminatory line between the features 66 | -------------------------------------------------------------------------------- /Week 1 - Intro to Python and Numpy/Python.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# CI-Fall2018 Python Tutorial" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "Tutorial By M. Doosti Lakhani" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "## Introduction" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "Python is a great general-purpose programming language on its own, but with the help of a few popular libraries (numpy, scipy, matplotlib) it becomes a powerful environment for scientific computing.\n", 29 | "\n", 30 | "This section will serve as a quick crash course both on the Python programming language and on the use of Python for scientific computing." 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "In this tutorial, we will cover:\n", 38 | "\n", 39 | "* Basic data types\n", 40 | "* Containers( Lists, Dictionaries, Sets, Tuples)\n", 41 | "* Functions\n", 42 | "* Classes\n", 43 | "* IPython: Creating notebooks\n", 44 | "* Github Integration" 45 | ] 46 | }, 47 | { 48 | "cell_type": "markdown", 49 | "metadata": {}, 50 | "source": [ 51 | "## Basics of Python" 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "metadata": {}, 57 | "source": [ 58 | "### Python versions" 59 | ] 60 | }, 61 | { 62 | "cell_type": "markdown", 63 | "metadata": {}, 64 | "source": [ 65 | "There are currently two different supported versions of Python, 2.7 and 3.7. Somewhat confusingly, Python 3.0 introduced many backwards-incompatible changes to the language, so code written for 2.7 may not work under 3.7 and vice versa. For this class all code will use Python 3.7.\n", 66 | "\n", 67 | "You can check your Python version at the command line by running `python --version`." 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "metadata": {}, 73 | "source": [ 74 | "### Basic data types" 75 | ] 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "metadata": {}, 80 | "source": [ 81 | "#### Numbers" 82 | ] 83 | }, 84 | { 85 | "cell_type": "markdown", 86 | "metadata": {}, 87 | "source": [ 88 | "Integers and floats work as you would expect from other languages:" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": 1, 94 | "metadata": {}, 95 | "outputs": [ 96 | { 97 | "name": "stdout", 98 | "output_type": "stream", 99 | "text": [ 100 | "3 \n" 101 | ] 102 | } 103 | ], 104 | "source": [ 105 | "x = 3\n", 106 | "print(x,type(x))" 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "execution_count": 2, 112 | "metadata": {}, 113 | "outputs": [ 114 | { 115 | "name": "stdout", 116 | "output_type": "stream", 117 | "text": [ 118 | "4\n", 119 | "2\n", 120 | "6\n", 121 | "9\n" 122 | ] 123 | } 124 | ], 125 | "source": [ 126 | "print (x + 1) # Addition;\n", 127 | "print (x - 1) # Subtraction;\n", 128 | "print (x * 2) # Multiplication;\n", 129 | "print (x ** 2) # Exponentiation;" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": 3, 135 | "metadata": {}, 136 | "outputs": [ 137 | { 138 | "name": "stdout", 139 | "output_type": "stream", 140 | "text": [ 141 | "4\n", 142 | "8\n" 143 | ] 144 | } 145 | ], 146 | "source": [ 147 | "x += 1\n", 148 | "print (x) # Prints \"4\"\n", 149 | "x *= 2\n", 150 | "print (x) # Prints \"8\"" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": 4, 156 | "metadata": {}, 157 | "outputs": [ 158 | { 159 | "name": "stdout", 160 | "output_type": "stream", 161 | "text": [ 162 | "\n", 163 | "2.5 3.5 5.0 6.25\n" 164 | ] 165 | } 166 | ], 167 | "source": [ 168 | "y = 2.5\n", 169 | "print (type(y)) # Prints \"\"\n", 170 | "print (y, y + 1, y * 2, y ** 2) # Prints \"2.5 3.5 5.0 6.25\"" 171 | ] 172 | }, 173 | { 174 | "cell_type": "markdown", 175 | "metadata": {}, 176 | "source": [ 177 | "Note that unlike many languages, Python does not have unary increment (x++) or decrement (x--) operators.\n", 178 | "\n", 179 | "Python also has built-in types for long integers and complex numbers" 180 | ] 181 | }, 182 | { 183 | "cell_type": "markdown", 184 | "metadata": {}, 185 | "source": [ 186 | "#### Booleans" 187 | ] 188 | }, 189 | { 190 | "cell_type": "markdown", 191 | "metadata": {}, 192 | "source": [ 193 | "Python implements all of the usual operators for Boolean logic, but uses English words rather than symbols (`&&`, `||`, etc.):" 194 | ] 195 | }, 196 | { 197 | "cell_type": "code", 198 | "execution_count": 5, 199 | "metadata": {}, 200 | "outputs": [ 201 | { 202 | "name": "stdout", 203 | "output_type": "stream", 204 | "text": [ 205 | "\n" 206 | ] 207 | } 208 | ], 209 | "source": [ 210 | "t, f = True, False\n", 211 | "print (type(t)) # Prints \"\"" 212 | ] 213 | }, 214 | { 215 | "cell_type": "markdown", 216 | "metadata": {}, 217 | "source": [ 218 | "Now we let's look at the operations:" 219 | ] 220 | }, 221 | { 222 | "cell_type": "code", 223 | "execution_count": 6, 224 | "metadata": {}, 225 | "outputs": [ 226 | { 227 | "name": "stdout", 228 | "output_type": "stream", 229 | "text": [ 230 | "False\n", 231 | "True\n", 232 | "False\n", 233 | "True\n" 234 | ] 235 | } 236 | ], 237 | "source": [ 238 | "print (t and f) # Logical AND;\n", 239 | "print (t or f) # Logical OR;\n", 240 | "print (not t) # Logical NOT;\n", 241 | "print (t != f) # Logical XOR;" 242 | ] 243 | }, 244 | { 245 | "cell_type": "markdown", 246 | "metadata": {}, 247 | "source": [ 248 | "#### Strings" 249 | ] 250 | }, 251 | { 252 | "cell_type": "code", 253 | "execution_count": 7, 254 | "metadata": {}, 255 | "outputs": [ 256 | { 257 | "name": "stdout", 258 | "output_type": "stream", 259 | "text": [ 260 | "hello world 5\n" 261 | ] 262 | } 263 | ], 264 | "source": [ 265 | "hello = 'hello'# String literals can use single quotes\n", 266 | "world = \"world\"# or double quotes; it does not matter.\n", 267 | "print (hello, world, len(hello))" 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": 8, 273 | "metadata": {}, 274 | "outputs": [ 275 | { 276 | "name": "stdout", 277 | "output_type": "stream", 278 | "text": [ 279 | "hello world\n" 280 | ] 281 | } 282 | ], 283 | "source": [ 284 | "hw = hello + ' ' + world # String concatenation\n", 285 | "print (hw) # prints \"hello world\"" 286 | ] 287 | }, 288 | { 289 | "cell_type": "code", 290 | "execution_count": 9, 291 | "metadata": {}, 292 | "outputs": [ 293 | { 294 | "name": "stdout", 295 | "output_type": "stream", 296 | "text": [ 297 | "hello world 12\n", 298 | "hello 12 world\n" 299 | ] 300 | } 301 | ], 302 | "source": [ 303 | "hw12 = '%s %s %d' % (hello, world, 12) # printf style string formatting\n", 304 | "print (hw12) # prints \"hello world 12\"\n", 305 | "print('{} {} {}'.format(hello, 12,world)) # You can use {}" 306 | ] 307 | }, 308 | { 309 | "cell_type": "markdown", 310 | "metadata": {}, 311 | "source": [ 312 | "String objects have a bunch of useful methods; for example:" 313 | ] 314 | }, 315 | { 316 | "cell_type": "code", 317 | "execution_count": 10, 318 | "metadata": {}, 319 | "outputs": [ 320 | { 321 | "name": "stdout", 322 | "output_type": "stream", 323 | "text": [ 324 | "Hello\n", 325 | "HELLO\n", 326 | " hello\n", 327 | " hello \n", 328 | "he(ell)(ell)o\n", 329 | "world\n" 330 | ] 331 | } 332 | ], 333 | "source": [ 334 | "s = \"hello\"\n", 335 | "print (s.capitalize()) # Capitalize a string; prints \"Hello\"\n", 336 | "print (s.upper()) # Convert a string to uppercase; prints \"HELLO\"\n", 337 | "print (s.rjust(7)) # Right-justify a string, padding with spaces; prints \" hello\"\n", 338 | "print (s.center(7)) # Center a string, padding with spaces; prints \" hello \"\n", 339 | "print (s.replace('l', '(ell)')) # Replace all instances of one substring with another;\n", 340 | " # prints \"he(ell)(ell)o\"\n", 341 | "print (' world '.strip()) # Strip leading and trailing whitespace; prints \"world\"" 342 | ] 343 | }, 344 | { 345 | "cell_type": "markdown", 346 | "metadata": {}, 347 | "source": [ 348 | "### Containers" 349 | ] 350 | }, 351 | { 352 | "cell_type": "markdown", 353 | "metadata": {}, 354 | "source": [ 355 | "Python includes several built-in container types: lists, dictionaries, sets, and tuples." 356 | ] 357 | }, 358 | { 359 | "cell_type": "markdown", 360 | "metadata": {}, 361 | "source": [ 362 | "#### Lists" 363 | ] 364 | }, 365 | { 366 | "cell_type": "markdown", 367 | "metadata": {}, 368 | "source": [ 369 | "A list is the Python equivalent of an array, but is resizeable and can contain elements of different types:" 370 | ] 371 | }, 372 | { 373 | "cell_type": "code", 374 | "execution_count": 12, 375 | "metadata": {}, 376 | "outputs": [ 377 | { 378 | "name": "stdout", 379 | "output_type": "stream", 380 | "text": [ 381 | "[1, 2, 3] 3\n", 382 | "3\n" 383 | ] 384 | } 385 | ], 386 | "source": [ 387 | "xs = [1,2,3] # Create a list\n", 388 | "print (xs, xs[2])\n", 389 | "print (xs[-1]) # Negative indices count from the end of the list; prints \"2\"" 390 | ] 391 | }, 392 | { 393 | "cell_type": "code", 394 | "execution_count": 13, 395 | "metadata": {}, 396 | "outputs": [ 397 | { 398 | "name": "stdout", 399 | "output_type": "stream", 400 | "text": [ 401 | "[1, 2, 'foo']\n" 402 | ] 403 | } 404 | ], 405 | "source": [ 406 | "xs[2] = 'foo' # Lists can contain elements of different types\n", 407 | "print (xs)" 408 | ] 409 | }, 410 | { 411 | "cell_type": "code", 412 | "execution_count": 14, 413 | "metadata": {}, 414 | "outputs": [ 415 | { 416 | "name": "stdout", 417 | "output_type": "stream", 418 | "text": [ 419 | "[1, 2, 'foo', 'bar']\n" 420 | ] 421 | } 422 | ], 423 | "source": [ 424 | "xs.append('bar') # Append a new element to the end of the list\n", 425 | "print (xs) " 426 | ] 427 | }, 428 | { 429 | "cell_type": "code", 430 | "execution_count": 15, 431 | "metadata": {}, 432 | "outputs": [ 433 | { 434 | "name": "stdout", 435 | "output_type": "stream", 436 | "text": [ 437 | "bar [1, 2, 'foo']\n" 438 | ] 439 | } 440 | ], 441 | "source": [ 442 | "x = xs.pop() # Remove and return the last element of the list\n", 443 | "print (x, xs) " 444 | ] 445 | }, 446 | { 447 | "cell_type": "markdown", 448 | "metadata": {}, 449 | "source": [ 450 | "#### Slicing" 451 | ] 452 | }, 453 | { 454 | "cell_type": "markdown", 455 | "metadata": {}, 456 | "source": [ 457 | "In addition to accessing list elements one at a time, Python provides concise syntax to access sublists; this is known as slicing:" 458 | ] 459 | }, 460 | { 461 | "cell_type": "code", 462 | "execution_count": 16, 463 | "metadata": {}, 464 | "outputs": [ 465 | { 466 | "name": "stdout", 467 | "output_type": "stream", 468 | "text": [ 469 | "[0, 1, 2, 3, 4]\n", 470 | "[2, 3, 4]\n", 471 | "[2, 3, 4]\n", 472 | "[0, 1, 2, 3, 4]\n", 473 | "[0, 1, 2, 3]\n", 474 | "[0, 1, 8, 9, 4]\n" 475 | ] 476 | } 477 | ], 478 | "source": [ 479 | "nums = list(range(5)) # range is a built-in function that creates a list of integers\n", 480 | "print (nums) # Prints \"[0, 1, 2, 3, 4]\"\n", 481 | "print (nums[2:5]) # Get a slice from index 2 to 4 (exclusive); prints \"[2, 3]\"\n", 482 | "print (nums[2:]) # Get a slice from index 2 to the end; prints \"[2, 3, 4]\" # Get a slice from the start to index 2 (exclusive); prints \"[0, 1]\"\n", 483 | "print (nums[:]) # Get a slice of the whole list; prints [\"0, 1, 2, 3, 4]\"\n", 484 | "print (nums[:-1]) # Slice indices can be negative; prints [\"0, 1, 2, 3]\"\n", 485 | "nums[2:4] = [8, 9] # Assign a new sublist to a slice\n", 486 | "print (nums) # Prints \"[0, 1, 8, 9, 4]\"" 487 | ] 488 | }, 489 | { 490 | "cell_type": "markdown", 491 | "metadata": {}, 492 | "source": [ 493 | "#### Loops" 494 | ] 495 | }, 496 | { 497 | "cell_type": "markdown", 498 | "metadata": {}, 499 | "source": [ 500 | "You can loop over the elements of a list like this:" 501 | ] 502 | }, 503 | { 504 | "cell_type": "code", 505 | "execution_count": 18, 506 | "metadata": {}, 507 | "outputs": [ 508 | { 509 | "name": "stdout", 510 | "output_type": "stream", 511 | "text": [ 512 | "cat\n", 513 | "dog\n", 514 | "monkey\n" 515 | ] 516 | } 517 | ], 518 | "source": [ 519 | "animals = ['cat', 'dog', 'monkey']\n", 520 | "for animal in animals:\n", 521 | " print(animal)" 522 | ] 523 | }, 524 | { 525 | "cell_type": "markdown", 526 | "metadata": {}, 527 | "source": [ 528 | "If you want access to the index of each element within the body of a loop, use the built-in `enumerate` function:" 529 | ] 530 | }, 531 | { 532 | "cell_type": "code", 533 | "execution_count": 19, 534 | "metadata": {}, 535 | "outputs": [ 536 | { 537 | "name": "stdout", 538 | "output_type": "stream", 539 | "text": [ 540 | "#1: cat\n", 541 | "#2: dog\n", 542 | "#3: monkey\n" 543 | ] 544 | } 545 | ], 546 | "source": [ 547 | "animals = ['cat', 'dog', 'monkey']\n", 548 | "for i, animal in enumerate(animals):\n", 549 | " print ('#{}: {}'.format(i + 1, animal))" 550 | ] 551 | }, 552 | { 553 | "cell_type": "code", 554 | "execution_count": 21, 555 | "metadata": {}, 556 | "outputs": [ 557 | { 558 | "name": "stdout", 559 | "output_type": "stream", 560 | "text": [ 561 | "3 -1\n" 562 | ] 563 | } 564 | ], 565 | "source": [ 566 | "def function(x,y , z = None):\n", 567 | " xx = x+y\n", 568 | " yy = x-y\n", 569 | " return (xx,yy)\n", 570 | "\n", 571 | "num1, num2 = function(1,2,4)\n", 572 | "print(num1, num2)" 573 | ] 574 | }, 575 | { 576 | "cell_type": "markdown", 577 | "metadata": {}, 578 | "source": [ 579 | "#### List comprehensions:" 580 | ] 581 | }, 582 | { 583 | "cell_type": "markdown", 584 | "metadata": {}, 585 | "source": [ 586 | "When programming, frequently we want to transform one type of data into another. As a simple example, consider the following code that computes square numbers:" 587 | ] 588 | }, 589 | { 590 | "cell_type": "code", 591 | "execution_count": 22, 592 | "metadata": {}, 593 | "outputs": [ 594 | { 595 | "name": "stdout", 596 | "output_type": "stream", 597 | "text": [ 598 | "[0, 1, 4, 9, 16]\n" 599 | ] 600 | } 601 | ], 602 | "source": [ 603 | "nums = [0, 1, 2, 3, 4]\n", 604 | "squares = []\n", 605 | "for x in nums:\n", 606 | " squares.append(x**2)\n", 607 | "print (squares)" 608 | ] 609 | }, 610 | { 611 | "cell_type": "markdown", 612 | "metadata": {}, 613 | "source": [ 614 | "You can make this code simpler using a list comprehension:" 615 | ] 616 | }, 617 | { 618 | "cell_type": "code", 619 | "execution_count": 23, 620 | "metadata": {}, 621 | "outputs": [ 622 | { 623 | "name": "stdout", 624 | "output_type": "stream", 625 | "text": [ 626 | "[(0, 0), (2, 0), (4, 0), (6, 0), (8, 0)]\n" 627 | ] 628 | } 629 | ], 630 | "source": [ 631 | "nums = [0, 1, 2, 3, 4]\n", 632 | "squares = [function(x,x) for x in nums]\n", 633 | "print (squares)" 634 | ] 635 | }, 636 | { 637 | "cell_type": "markdown", 638 | "metadata": {}, 639 | "source": [ 640 | "List comprehensions can also contain conditions:" 641 | ] 642 | }, 643 | { 644 | "cell_type": "code", 645 | "execution_count": 24, 646 | "metadata": {}, 647 | "outputs": [ 648 | { 649 | "name": "stdout", 650 | "output_type": "stream", 651 | "text": [ 652 | "[0, 4, 16]\n" 653 | ] 654 | } 655 | ], 656 | "source": [ 657 | "nums = [0, 1, 2, 3, 4]\n", 658 | "even_squares = [x ** 2 for x in nums if x % 2 == 0]\n", 659 | "print (even_squares)" 660 | ] 661 | }, 662 | { 663 | "cell_type": "markdown", 664 | "metadata": {}, 665 | "source": [ 666 | "#### Dictionaries" 667 | ] 668 | }, 669 | { 670 | "cell_type": "markdown", 671 | "metadata": {}, 672 | "source": [ 673 | "A dictionary stores (key, value) pairs, similar to a `Map` in Java or an object in Javascript. You can use it like this:" 674 | ] 675 | }, 676 | { 677 | "cell_type": "code", 678 | "execution_count": 25, 679 | "metadata": {}, 680 | "outputs": [ 681 | { 682 | "name": "stdout", 683 | "output_type": "stream", 684 | "text": [ 685 | "moaw\n", 686 | "True\n" 687 | ] 688 | } 689 | ], 690 | "source": [ 691 | "d= {'cat':'moaw', 'dog':'bark'} # Create a new dictionary\n", 692 | "print (d['cat']) # Get an entry from a dictionary; prints \"cute\"\n", 693 | "print ('cat' in d) # Check if a dictionary has a given key; prints \"True\"" 694 | ] 695 | }, 696 | { 697 | "cell_type": "code", 698 | "execution_count": 26, 699 | "metadata": {}, 700 | "outputs": [ 701 | { 702 | "name": "stdout", 703 | "output_type": "stream", 704 | "text": [ 705 | "water\n", 706 | "{'cat': 'moaw', 'dog': 'bark', 'fish': 'water'}\n" 707 | ] 708 | } 709 | ], 710 | "source": [ 711 | "d['fish'] = 'water' # Set an entry in a dictionary\n", 712 | "print (d['fish']) # Prints \"wet\"\n", 713 | "print(d)" 714 | ] 715 | }, 716 | { 717 | "cell_type": "code", 718 | "execution_count": 27, 719 | "metadata": {}, 720 | "outputs": [ 721 | { 722 | "ename": "KeyError", 723 | "evalue": "'monkey'", 724 | "output_type": "error", 725 | "traceback": [ 726 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 727 | "\u001b[1;31mKeyError\u001b[0m Traceback (most recent call last)", 728 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mprint\u001b[0m \u001b[1;33m(\u001b[0m\u001b[0md\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'monkey'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;31m# KeyError: 'monkey' not a key of d\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", 729 | "\u001b[1;31mKeyError\u001b[0m: 'monkey'" 730 | ] 731 | } 732 | ], 733 | "source": [ 734 | "print (d['monkey']) # KeyError: 'monkey' not a key of d" 735 | ] 736 | }, 737 | { 738 | "cell_type": "code", 739 | "execution_count": 28, 740 | "metadata": {}, 741 | "outputs": [ 742 | { 743 | "name": "stdout", 744 | "output_type": "stream", 745 | "text": [ 746 | "N/A\n", 747 | "water\n" 748 | ] 749 | } 750 | ], 751 | "source": [ 752 | "print (d.get('monkey', 'N/A')) # Get an element with a default; prints \"N/A\"\n", 753 | "print (d.get('fish', 'N/A')) # Get an element with a default; prints \"wet\"" 754 | ] 755 | }, 756 | { 757 | "cell_type": "code", 758 | "execution_count": 29, 759 | "metadata": {}, 760 | "outputs": [ 761 | { 762 | "name": "stdout", 763 | "output_type": "stream", 764 | "text": [ 765 | "N/A\n" 766 | ] 767 | } 768 | ], 769 | "source": [ 770 | "del d['fish'] # Remove an element from a dictionary\n", 771 | "print (d.get('fish', 'N/A')) # \"fish\" is no longer a key; prints \"N/A\"" 772 | ] 773 | }, 774 | { 775 | "cell_type": "markdown", 776 | "metadata": {}, 777 | "source": [ 778 | "It is easy to iterate over the keys in a dictionary:" 779 | ] 780 | }, 781 | { 782 | "cell_type": "code", 783 | "execution_count": 30, 784 | "metadata": {}, 785 | "outputs": [ 786 | { 787 | "name": "stdout", 788 | "output_type": "stream", 789 | "text": [ 790 | "A person has 2 legs\n", 791 | "A cat has 4 legs\n", 792 | "A spider has 8 legs\n" 793 | ] 794 | } 795 | ], 796 | "source": [ 797 | "d = {'person': 2, 'cat': 4, 'spider': 8}\n", 798 | "for animal in d:\n", 799 | " legs = d[animal]\n", 800 | " print ('A {} has {} legs'.format(animal, legs))" 801 | ] 802 | }, 803 | { 804 | "cell_type": "markdown", 805 | "metadata": {}, 806 | "source": [ 807 | "Dictionary comprehensions: These are similar to list comprehensions, but allow you to easily construct dictionaries. For example:" 808 | ] 809 | }, 810 | { 811 | "cell_type": "code", 812 | "execution_count": 31, 813 | "metadata": {}, 814 | "outputs": [ 815 | { 816 | "name": "stdout", 817 | "output_type": "stream", 818 | "text": [ 819 | "{0: 0, 2: 4, 4: 16}\n" 820 | ] 821 | } 822 | ], 823 | "source": [ 824 | "nums = [0, 1, 2, 3, 4]\n", 825 | "even_num_to_square = {x: x ** 2 for x in nums if x % 2 == 0}\n", 826 | "print(even_num_to_square)" 827 | ] 828 | }, 829 | { 830 | "cell_type": "markdown", 831 | "metadata": {}, 832 | "source": [ 833 | "#### Sets" 834 | ] 835 | }, 836 | { 837 | "cell_type": "markdown", 838 | "metadata": {}, 839 | "source": [ 840 | "A set is an unordered collection of distinct elements. As a simple example, consider the following:" 841 | ] 842 | }, 843 | { 844 | "cell_type": "code", 845 | "execution_count": 32, 846 | "metadata": {}, 847 | "outputs": [ 848 | { 849 | "name": "stdout", 850 | "output_type": "stream", 851 | "text": [ 852 | "True\n", 853 | "False\n" 854 | ] 855 | } 856 | ], 857 | "source": [ 858 | "animals = {'cat', 'dog'} # set of `cat` and `dog`\n", 859 | "print ('cat' in animals) # Check if an element is in a set; prints \"True\"\n", 860 | "print ('fish' in animals) # prints \"False\"\n" 861 | ] 862 | }, 863 | { 864 | "cell_type": "code", 865 | "execution_count": 33, 866 | "metadata": {}, 867 | "outputs": [ 868 | { 869 | "name": "stdout", 870 | "output_type": "stream", 871 | "text": [ 872 | "True\n", 873 | "3\n" 874 | ] 875 | } 876 | ], 877 | "source": [ 878 | "animals.add('fish') # Add an element to a set\n", 879 | "print ('fish' in animals)\n", 880 | "print (len(animals) ) # Number of elements in a set;" 881 | ] 882 | }, 883 | { 884 | "cell_type": "code", 885 | "execution_count": 34, 886 | "metadata": {}, 887 | "outputs": [ 888 | { 889 | "name": "stdout", 890 | "output_type": "stream", 891 | "text": [ 892 | "3\n", 893 | "2\n" 894 | ] 895 | } 896 | ], 897 | "source": [ 898 | "animals.add('cat') # Adding an element that is already in the set does nothing\n", 899 | "print (len(animals)) \n", 900 | "animals.remove('cat') # Remove an element from a set\n", 901 | "print (len(animals)) " 902 | ] 903 | }, 904 | { 905 | "cell_type": "markdown", 906 | "metadata": {}, 907 | "source": [ 908 | "_Loops_: Iterating over a set has the same syntax as iterating over a list; however since sets are unordered, you cannot make assumptions about the order in which you visit the elements of the set:" 909 | ] 910 | }, 911 | { 912 | "cell_type": "code", 913 | "execution_count": 35, 914 | "metadata": {}, 915 | "outputs": [ 916 | { 917 | "name": "stdout", 918 | "output_type": "stream", 919 | "text": [ 920 | "#1: fish\n", 921 | "#2: dog\n", 922 | "#3: cat\n" 923 | ] 924 | } 925 | ], 926 | "source": [ 927 | "animals = {'cat', 'dog', 'fish'}\n", 928 | "for idx, animal in enumerate(animals):\n", 929 | " print ('#{}: {}'.format(idx + 1, animal))\n", 930 | "# Prints \"#1: fish\", \"#2: dog\", \"#3: cat\"" 931 | ] 932 | }, 933 | { 934 | "cell_type": "markdown", 935 | "metadata": {}, 936 | "source": [ 937 | "Set comprehensions: Like lists and dictionaries, we can easily construct sets using set comprehensions:" 938 | ] 939 | }, 940 | { 941 | "cell_type": "code", 942 | "execution_count": 37, 943 | "metadata": {}, 944 | "outputs": [ 945 | { 946 | "name": "stdout", 947 | "output_type": "stream", 948 | "text": [ 949 | "{0.0, 1.0, 2.0, 1.7320508075688772, 1.4142135623730951}\n" 950 | ] 951 | } 952 | ], 953 | "source": [ 954 | "from math import sqrt\n", 955 | "print ({sqrt(x) for x in range(5)})" 956 | ] 957 | }, 958 | { 959 | "cell_type": "markdown", 960 | "metadata": {}, 961 | "source": [ 962 | "#### Tuples" 963 | ] 964 | }, 965 | { 966 | "cell_type": "markdown", 967 | "metadata": {}, 968 | "source": [ 969 | "A tuple is an (immutable) ordered list of values. A tuple is in many ways similar to a list; one of the most important differences is that tuples can be used as keys in dictionaries and as elements of sets, while lists cannot. Here is a trivial example:" 970 | ] 971 | }, 972 | { 973 | "cell_type": "code", 974 | "execution_count": 38, 975 | "metadata": {}, 976 | "outputs": [ 977 | { 978 | "name": "stdout", 979 | "output_type": "stream", 980 | "text": [ 981 | "\n", 982 | "5\n", 983 | "1\n" 984 | ] 985 | } 986 | ], 987 | "source": [ 988 | "d = {(x, x + 1): x for x in range(10)} # Create a dictionary with tuple keys\n", 989 | "t = (5, 6) # Create a tuple\n", 990 | "print (type(t))\n", 991 | "print (d[t]) \n", 992 | "print (d[(1, 2)])" 993 | ] 994 | }, 995 | { 996 | "cell_type": "code", 997 | "execution_count": 39, 998 | "metadata": {}, 999 | "outputs": [ 1000 | { 1001 | "ename": "TypeError", 1002 | "evalue": "'tuple' object does not support item assignment", 1003 | "output_type": "error", 1004 | "traceback": [ 1005 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 1006 | "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", 1007 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mt\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;36m1\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", 1008 | "\u001b[1;31mTypeError\u001b[0m: 'tuple' object does not support item assignment" 1009 | ] 1010 | } 1011 | ], 1012 | "source": [ 1013 | "t[0] = 1" 1014 | ] 1015 | }, 1016 | { 1017 | "cell_type": "markdown", 1018 | "metadata": {}, 1019 | "source": [ 1020 | "### Functions" 1021 | ] 1022 | }, 1023 | { 1024 | "cell_type": "markdown", 1025 | "metadata": {}, 1026 | "source": [ 1027 | "Python functions are defined using the `def` keyword. For example:" 1028 | ] 1029 | }, 1030 | { 1031 | "cell_type": "code", 1032 | "execution_count": 41, 1033 | "metadata": {}, 1034 | "outputs": [ 1035 | { 1036 | "name": "stdout", 1037 | "output_type": "stream", 1038 | "text": [ 1039 | "-1\n", 1040 | "0\n", 1041 | "1\n" 1042 | ] 1043 | } 1044 | ], 1045 | "source": [ 1046 | "def sign(x):\n", 1047 | " if x > 0:\n", 1048 | " return 1\n", 1049 | " elif x < 0:\n", 1050 | " return -1\n", 1051 | " else:\n", 1052 | " return 0\n", 1053 | "\n", 1054 | "for x in [-100, 0, 100]:\n", 1055 | " print (sign(x))" 1056 | ] 1057 | }, 1058 | { 1059 | "cell_type": "markdown", 1060 | "metadata": {}, 1061 | "source": [ 1062 | "We will often define functions to take optional keyword arguments, like this:" 1063 | ] 1064 | }, 1065 | { 1066 | "cell_type": "code", 1067 | "execution_count": 51, 1068 | "metadata": {}, 1069 | "outputs": [ 1070 | { 1071 | "name": "stdout", 1072 | "output_type": "stream", 1073 | "text": [ 1074 | "Hello, Nik!\n", 1075 | "HELLO, NIK\n" 1076 | ] 1077 | } 1078 | ], 1079 | "source": [ 1080 | "def hello(name, loud=False):\n", 1081 | " if loud:\n", 1082 | " print ('HELLO, {}'.format(name.upper()))\n", 1083 | " else:\n", 1084 | " print ('Hello, {}!'.format(name))\n", 1085 | "\n", 1086 | "hello('Nik')\n", 1087 | "hello('Nik', loud=True)" 1088 | ] 1089 | }, 1090 | { 1091 | "cell_type": "markdown", 1092 | "metadata": {}, 1093 | "source": [ 1094 | "### Classes" 1095 | ] 1096 | }, 1097 | { 1098 | "cell_type": "markdown", 1099 | "metadata": {}, 1100 | "source": [ 1101 | "The syntax for defining classes in Python is straightforward:" 1102 | ] 1103 | }, 1104 | { 1105 | "cell_type": "code", 1106 | "execution_count": 50, 1107 | "metadata": {}, 1108 | "outputs": [ 1109 | { 1110 | "name": "stdout", 1111 | "output_type": "stream", 1112 | "text": [ 1113 | "Hello, Nik!\n", 1114 | "HELLO, NIK\n" 1115 | ] 1116 | } 1117 | ], 1118 | "source": [ 1119 | "class Greeter:\n", 1120 | " \n", 1121 | " # Constructor\n", 1122 | " def __init__(self, name):\n", 1123 | " self.name = name # Create an instance variable\n", 1124 | " \n", 1125 | " # Instance method\n", 1126 | " def hello(self, loud=False):\n", 1127 | " if loud:\n", 1128 | " print ('HELLO, {}'.format(self.name.upper()))\n", 1129 | " else:\n", 1130 | " print ('Hello, {}!'.format(self.name))\n", 1131 | " def hi(self):\n", 1132 | " print('hi',self.name)\n", 1133 | " \n", 1134 | "g = Greeter('Nik') # Construct an instance of the Greeter class\n", 1135 | "g.hello() # Call an instance method; prints \"Hello, Fred\"\n", 1136 | "g.hello(loud=True) # Call an instance method; prints \"HELLO, FRED!\"" 1137 | ] 1138 | }, 1139 | { 1140 | "cell_type": "code", 1141 | "execution_count": 60, 1142 | "metadata": {}, 1143 | "outputs": [], 1144 | "source": [ 1145 | "class DGreeter(Greeter):\n", 1146 | " def __init__(self,name):\n", 1147 | " Greeter.__init__(self, name)\n", 1148 | " \n", 1149 | " def double_hello(self):\n", 1150 | " print('Hello Hello',self.name.upper())\n", 1151 | " \n", 1152 | " def hi(self):\n", 1153 | " print('hi hi hi',self.name)" 1154 | ] 1155 | }, 1156 | { 1157 | "cell_type": "code", 1158 | "execution_count": 61, 1159 | "metadata": {}, 1160 | "outputs": [ 1161 | { 1162 | "name": "stdout", 1163 | "output_type": "stream", 1164 | "text": [ 1165 | "Hello Hello NIK\n", 1166 | "Hello, Nik!\n", 1167 | "hi hi hi Nik\n" 1168 | ] 1169 | } 1170 | ], 1171 | "source": [ 1172 | "d = DGreeter('Nik')\n", 1173 | "d.double_hello()\n", 1174 | "d.hello()\n", 1175 | "d.hi()" 1176 | ] 1177 | } 1178 | ], 1179 | "metadata": { 1180 | "kernelspec": { 1181 | "display_name": "Python 3", 1182 | "language": "python", 1183 | "name": "python3" 1184 | }, 1185 | "language_info": { 1186 | "codemirror_mode": { 1187 | "name": "ipython", 1188 | "version": 3 1189 | }, 1190 | "file_extension": ".py", 1191 | "mimetype": "text/x-python", 1192 | "name": "python", 1193 | "nbconvert_exporter": "python", 1194 | "pygments_lexer": "ipython3", 1195 | "version": "3.6.6" 1196 | } 1197 | }, 1198 | "nbformat": 4, 1199 | "nbformat_minor": 2 1200 | } 1201 | -------------------------------------------------------------------------------- /Week 2 - Genetic Algorithm(part 1)/Genetic Operators.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "slideshow": { 7 | "slide_type": "slide" 8 | } 9 | }, 10 | "source": [ 11 | "# Content\n", 12 | "* Importing Libraries\n", 13 | "* Introduction to Problem\n", 14 | "* Knapsack Sample Generator\n", 15 | "* Chromosome Design\n", 16 | "* Simple Genetic Algorithm Architecture\n", 17 | "* Initialization\n", 18 | "* Fitness Calculation\n", 19 | "* Selection\n", 20 | "* Implemented Functions Test\n", 21 | "* Next Week" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "# Importing Libraries" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 1, 34 | "metadata": { 35 | "slideshow": { 36 | "slide_type": "slide" 37 | } 38 | }, 39 | "outputs": [], 40 | "source": [ 41 | "import numpy as np\n", 42 | "from matplotlib.pyplot import pie" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "metadata": { 48 | "slideshow": { 49 | "slide_type": "slide" 50 | } 51 | }, 52 | "source": [ 53 | "# Introduction to Problem\n", 54 | "The problem that we gonna solve is the Knapsack Problem.\n", 55 | "

The Knapsack optimization is a classical algorithm problem. You have two things: a bag with a size (the weight it can hold) and a set of boxes with different weights and different values. The goal is to fill the bag to make it as valuable as possible without exceeding the maximum weight. It is a famous mathematical problem since 1972. The genetic algorithm is well suited to solve that because it’s an optimization problem with a lot of possible solutions(1).

" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": { 61 | "slideshow": { 62 | "slide_type": "-" 63 | } 64 | }, 65 | "source": [ 66 | "![knapsack](figs/knapsack_problem.png)" 67 | ] 68 | }, 69 | { 70 | "cell_type": "markdown", 71 | "metadata": {}, 72 | "source": [ 73 | "# Simple Genetic Algorithm Architecture" 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "metadata": {}, 79 | "source": [ 80 | "The overall Architecture of the genetic algorithm, that we will implement is as follow :" 81 | ] 82 | }, 83 | { 84 | "cell_type": "markdown", 85 | "metadata": {}, 86 | "source": [ 87 | "![genetic_architecture](figs/genetic_architecture.PNG)" 88 | ] 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "metadata": { 93 | "slideshow": { 94 | "slide_type": "slide" 95 | } 96 | }, 97 | "source": [ 98 | "# Knapsack Sample Generator\n", 99 | "First we should implement 2 classes as follow :\n", 100 | "* First class called Item represent an item which a have two features :\n", 101 | " * Value\n", 102 | " * Weight\n", 103 | "* Second class called Bag represent a bag which have one feature, capacity." 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": 2, 109 | "metadata": {}, 110 | "outputs": [], 111 | "source": [ 112 | "class Item:\n", 113 | " def __init__(self, value, weight) :\n", 114 | " self.value = value\n", 115 | " self.weight = weight\n", 116 | "\n", 117 | "class Bag:\n", 118 | " def __init__(self, capacity) :\n", 119 | " self.capacity = capacity" 120 | ] 121 | }, 122 | { 123 | "cell_type": "markdown", 124 | "metadata": {}, 125 | "source": [ 126 | "Now we should generate two things :\n", 127 | "* A Bag with randomly chosen capacity\n", 128 | "* A random number of Items initialized with randomly chosen weights and values " 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": 3, 134 | "metadata": {}, 135 | "outputs": [ 136 | { 137 | "name": "stdout", 138 | "output_type": "stream", 139 | "text": [ 140 | "We have 12 items with weight and values of :\n", 141 | "item 0: Weight=>33.77062994324069 Value=>17.78533854675055\n", 142 | "item 1: Weight=>33.890069551365016 Value=>25.738368528682702\n", 143 | "item 2: Weight=>15.375268291707993 Value=>18.70691090357917\n", 144 | "item 3: Weight=>2.2685190926977272 Value=>8.926038196334169\n", 145 | "item 4: Weight=>19.106604692853995 Value=>8.179688837403397\n", 146 | "item 5: Weight=>19.199086895002292 Value=>24.365061863264796\n", 147 | "item 6: Weight=>33.4431505414951 Value=>11.783543883024892\n", 148 | "item 7: Weight=>25.926874882047887 Value=>10.12188481251805\n", 149 | "item 8: Weight=>38.28620635812185 Value=>11.04724619521644\n", 150 | "item 9: Weight=>34.803490334337454 Value=>4.210523412379354\n", 151 | "item 10: Weight=>32.03643007918577 Value=>14.208241358211314\n", 152 | "item 11: Weight=>27.155181204758414 Value=>15.614324386536145\n", 153 | "We have a bag with capacity of 385.90367426844006\n" 154 | ] 155 | } 156 | ], 157 | "source": [ 158 | "np.random.seed(0)\n", 159 | "items_number = np.random.randint(30)\n", 160 | "max_value = 30\n", 161 | "max_weight = 40\n", 162 | "# List Comprehensions, for more details see : https://www.pythonforbeginners.com/basics/list-comprehensions-in-python\n", 163 | "items = np.array([Item(np.random.rand()*max_value, np.random.rand()*max_weight) for _ in range(items_number)])\n", 164 | "bag = Bag(np.random.rand()*(max_weight*len(items)) + max_weight)\n", 165 | "print('We have {} items with weight and values of :'.format(len(items)))\n", 166 | "for i,item in enumerate(items) :\n", 167 | " print('item {}: Weight=>{} Value=>{}'.format(i, item.weight, item.value))\n", 168 | "print('We have a bag with capacity of {}'.format(bag.capacity))" 169 | ] 170 | }, 171 | { 172 | "cell_type": "markdown", 173 | "metadata": {}, 174 | "source": [ 175 | "# Chromosome Design" 176 | ] 177 | }, 178 | { 179 | "cell_type": "markdown", 180 | "metadata": {}, 181 | "source": [ 182 | "For solving a problem using Genetic Algorithms, the first step we should take is to find a good structure that represents our search space as best as possible" 183 | ] 184 | }, 185 | { 186 | "cell_type": "markdown", 187 | "metadata": {}, 188 | "source": [ 189 | "In knapsack problem, every item can be two different states, Selected and not Selected, So we can represent a binary chromosome which its number of genes is equal to the number of items we have." 190 | ] 191 | }, 192 | { 193 | "cell_type": "markdown", 194 | "metadata": {}, 195 | "source": [ 196 | "![chromosome](figs/chromosome.png)" 197 | ] 198 | }, 199 | { 200 | "cell_type": "code", 201 | "execution_count": 4, 202 | "metadata": {}, 203 | "outputs": [], 204 | "source": [ 205 | "class Chromosome :\n", 206 | " def __init__(self, length) :\n", 207 | " self.genes = np.random.rand(length) > .5\n", 208 | " self.fitness = float('-inf')\n", 209 | " \n", 210 | " def __len__(self) :\n", 211 | " return len(self.genes)\n", 212 | " \n", 213 | " def reset(self) :\n", 214 | " self.fitness = float('-inf')" 215 | ] 216 | }, 217 | { 218 | "cell_type": "markdown", 219 | "metadata": {}, 220 | "source": [ 221 | "# Initialization" 222 | ] 223 | }, 224 | { 225 | "cell_type": "markdown", 226 | "metadata": {}, 227 | "source": [ 228 | "In this step we will initialize the First Generation :" 229 | ] 230 | }, 231 | { 232 | "cell_type": "code", 233 | "execution_count": 5, 234 | "metadata": {}, 235 | "outputs": [], 236 | "source": [ 237 | "population_init = lambda size, chrom_size : np.array([Chromosome(chrom_size) for _ in range(size)])" 238 | ] 239 | }, 240 | { 241 | "cell_type": "markdown", 242 | "metadata": {}, 243 | "source": [ 244 | "# Fitness Calculation" 245 | ] 246 | }, 247 | { 248 | "cell_type": "markdown", 249 | "metadata": {}, 250 | "source": [ 251 | "Calculating fitness is as follow :\n", 252 | "* If weights of selected items were more than the capacity of the bag, the fitness will be -1\n", 253 | "* Otherwise, the fitness will be equal to the sum of the value of the selected items" 254 | ] 255 | }, 256 | { 257 | "cell_type": "code", 258 | "execution_count": 10, 259 | "metadata": {}, 260 | "outputs": [], 261 | "source": [ 262 | "def fitness_eval(chrom, items, bag, epsilon=2) :\n", 263 | " selected_items = items[chrom.genes]\n", 264 | " capacity_full = 0\n", 265 | " fitness = 0\n", 266 | " for item in selected_items :\n", 267 | " capacity_full += item.weight\n", 268 | " if capacity_full > bag.capacity :\n", 269 | " fitness = epsilon\n", 270 | " break\n", 271 | " fitness += item.value\n", 272 | " return fitness" 273 | ] 274 | }, 275 | { 276 | "cell_type": "markdown", 277 | "metadata": {}, 278 | "source": [ 279 | "# Selection" 280 | ] 281 | }, 282 | { 283 | "cell_type": "markdown", 284 | "metadata": {}, 285 | "source": [ 286 | "There are various selection methods out there including :\n", 287 | "* Random Selection\n", 288 | "* Proportional Selection\n", 289 | " * Roulette wheel selection\n", 290 | " * Stochastic Universal Sampling\n", 291 | "* Tournament Selection\n", 292 | "* Rank-Based Selection\n", 293 | "* Boltzmann Selection\n", 294 | "But in this session, we will implement roulette wheel selection and tournament selection." 295 | ] 296 | }, 297 | { 298 | "cell_type": "markdown", 299 | "metadata": {}, 300 | "source": [ 301 | "## Roulette Selection\n", 302 | "![roulette_wheel](figs/roulette_wheel.png \"Roulette Wheel Selection\")" 303 | ] 304 | }, 305 | { 306 | "cell_type": "markdown", 307 | "metadata": {}, 308 | "source": [ 309 | "You can read more about Roulette Selection and other Selection Algorithms here." 310 | ] 311 | }, 312 | { 313 | "cell_type": "code", 314 | "execution_count": 7, 315 | "metadata": {}, 316 | "outputs": [], 317 | "source": [ 318 | "def roulette_selection(pop) :\n", 319 | " i = 0\n", 320 | " fitnesses = np.array(list(map(lambda c: c.fitness, pop)))\n", 321 | " sum_of_fitnesses = np.sum(fitnesses)\n", 322 | " sel_prob = fitnesses/sum_of_fitnesses\n", 323 | " pie(sel_prob) # Ploting pie chart of probablity of each individual\n", 324 | " sum_prob = sel_prob[i]\n", 325 | " pointer = np.random.rand()\n", 326 | " while sum_prob < pointer :\n", 327 | " i += 1\n", 328 | " sum_prob += sel_prob[i] \n", 329 | " return pop[i]" 330 | ] 331 | }, 332 | { 333 | "cell_type": "markdown", 334 | "metadata": {}, 335 | "source": [ 336 | "## Tournament Selection" 337 | ] 338 | }, 339 | { 340 | "cell_type": "markdown", 341 | "metadata": {}, 342 | "source": [ 343 | "![tournoment_selection](figs/tournament_selection.jpg)" 344 | ] 345 | }, 346 | { 347 | "cell_type": "markdown", 348 | "metadata": {}, 349 | "source": [ 350 | "You can read more about Tournament Selection here." 351 | ] 352 | }, 353 | { 354 | "cell_type": "code", 355 | "execution_count": 8, 356 | "metadata": {}, 357 | "outputs": [], 358 | "source": [ 359 | "tournament_selection = lambda pop, sel_pressure: max(np.random.choice(pop, sel_pressure),key=lambda c: c.fitness)" 360 | ] 361 | }, 362 | { 363 | "cell_type": "markdown", 364 | "metadata": {}, 365 | "source": [ 366 | "# Implemented Functions Test" 367 | ] 368 | }, 369 | { 370 | "cell_type": "code", 371 | "execution_count": 9, 372 | "metadata": {}, 373 | "outputs": [ 374 | { 375 | "name": "stdout", 376 | "output_type": "stream", 377 | "text": [ 378 | "Selected Individual Fitness: 85.7276106440008\n", 379 | "Genes: [False False True False False True True False True True False True]\n" 380 | ] 381 | }, 382 | { 383 | "data": { 384 | "image/png": "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\n", 385 | "text/plain": [ 386 | "" 387 | ] 388 | }, 389 | "metadata": {}, 390 | "output_type": "display_data" 391 | } 392 | ], 393 | "source": [ 394 | "pop_size = 50\n", 395 | "selection_method = '' # You can change this to 'tournament' for changing the selection function\n", 396 | "# Initializing First Generation\n", 397 | "pop = population_init(pop_size, len(items))\n", 398 | "\n", 399 | "# Calculating Fitness of Each Individual\n", 400 | "for chrom in pop :\n", 401 | " chrom.fitness = fitness_eval(chrom)\n", 402 | "\n", 403 | "# Selecting just an individual for ensuring that implemented functions work properly\n", 404 | "if selection_method == 'tournament' :\n", 405 | " sel_ind = tournament_selection(pop, 10)\n", 406 | "else :\n", 407 | " sel_ind = roulette_selection(pop)\n", 408 | "print('Selected Individual Fitness: {}\\nGenes: {}'.format(sel_ind.fitness, sel_ind.genes))" 409 | ] 410 | }, 411 | { 412 | "cell_type": "markdown", 413 | "metadata": {}, 414 | "source": [ 415 | "# Next Week" 416 | ] 417 | }, 418 | { 419 | "cell_type": "markdown", 420 | "metadata": {}, 421 | "source": [ 422 | "Next week, we will implement :\n", 423 | "* Different Crossover Operations\n", 424 | "* Different Mutation Operations\n", 425 | "* Complete Genetic Alogirhtm for solving this problem" 426 | ] 427 | } 428 | ], 429 | "metadata": { 430 | "kernelspec": { 431 | "display_name": "Python 3", 432 | "language": "python", 433 | "name": "python3" 434 | }, 435 | "language_info": { 436 | "codemirror_mode": { 437 | "name": "ipython", 438 | "version": 3 439 | }, 440 | "file_extension": ".py", 441 | "mimetype": "text/x-python", 442 | "name": "python", 443 | "nbconvert_exporter": "python", 444 | "pygments_lexer": "ipython3", 445 | "version": "3.6.4" 446 | } 447 | }, 448 | "nbformat": 4, 449 | "nbformat_minor": 2 450 | } 451 | -------------------------------------------------------------------------------- /Week 2 - Genetic Algorithm(part 1)/figs/chromosome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Computational-Intelligence-Fall18/Computational-Intelligence-Tutorials/18018dd5258ec0563513076c8e46bba8113d65b4/Week 2 - Genetic Algorithm(part 1)/figs/chromosome.png -------------------------------------------------------------------------------- /Week 2 - Genetic Algorithm(part 1)/figs/genetic_architecture.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Computational-Intelligence-Fall18/Computational-Intelligence-Tutorials/18018dd5258ec0563513076c8e46bba8113d65b4/Week 2 - Genetic Algorithm(part 1)/figs/genetic_architecture.PNG -------------------------------------------------------------------------------- /Week 2 - Genetic Algorithm(part 1)/figs/knapsack_problem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Computational-Intelligence-Fall18/Computational-Intelligence-Tutorials/18018dd5258ec0563513076c8e46bba8113d65b4/Week 2 - Genetic Algorithm(part 1)/figs/knapsack_problem.png -------------------------------------------------------------------------------- /Week 2 - Genetic Algorithm(part 1)/figs/roulette_wheel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Computational-Intelligence-Fall18/Computational-Intelligence-Tutorials/18018dd5258ec0563513076c8e46bba8113d65b4/Week 2 - Genetic Algorithm(part 1)/figs/roulette_wheel.png -------------------------------------------------------------------------------- /Week 2 - Genetic Algorithm(part 1)/figs/tournament_selection.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Computational-Intelligence-Fall18/Computational-Intelligence-Tutorials/18018dd5258ec0563513076c8e46bba8113d65b4/Week 2 - Genetic Algorithm(part 1)/figs/tournament_selection.jpg -------------------------------------------------------------------------------- /Week 3 - Genetic Algorithm(part 2)/None0000000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Computational-Intelligence-Fall18/Computational-Intelligence-Tutorials/18018dd5258ec0563513076c8e46bba8113d65b4/Week 3 - Genetic Algorithm(part 2)/None0000000.png -------------------------------------------------------------------------------- /Week 3 - Genetic Algorithm(part 2)/figs/genetic_architecture.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Computational-Intelligence-Fall18/Computational-Intelligence-Tutorials/18018dd5258ec0563513076c8e46bba8113d65b4/Week 3 - Genetic Algorithm(part 2)/figs/genetic_architecture.PNG -------------------------------------------------------------------------------- /Week 3 - Genetic Algorithm(part 2)/figs/one_point_crossover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Computational-Intelligence-Fall18/Computational-Intelligence-Tutorials/18018dd5258ec0563513076c8e46bba8113d65b4/Week 3 - Genetic Algorithm(part 2)/figs/one_point_crossover.png -------------------------------------------------------------------------------- /Week 3 - Genetic Algorithm(part 2)/figs/two_point_crossover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Computational-Intelligence-Fall18/Computational-Intelligence-Tutorials/18018dd5258ec0563513076c8e46bba8113d65b4/Week 3 - Genetic Algorithm(part 2)/figs/two_point_crossover.png -------------------------------------------------------------------------------- /Week 3 - Genetic Algorithm(part 2)/figs/two_point_crossover_v1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Computational-Intelligence-Fall18/Computational-Intelligence-Tutorials/18018dd5258ec0563513076c8e46bba8113d65b4/Week 3 - Genetic Algorithm(part 2)/figs/two_point_crossover_v1.jpg -------------------------------------------------------------------------------- /Week 3 - Genetic Algorithm(part 2)/figs/two_point_crossover_v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Computational-Intelligence-Fall18/Computational-Intelligence-Tutorials/18018dd5258ec0563513076c8e46bba8113d65b4/Week 3 - Genetic Algorithm(part 2)/figs/two_point_crossover_v2.png -------------------------------------------------------------------------------- /Week 3 - Genetic Algorithm(part 2)/figs/uniform-crossover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Computational-Intelligence-Fall18/Computational-Intelligence-Tutorials/18018dd5258ec0563513076c8e46bba8113d65b4/Week 3 - Genetic Algorithm(part 2)/figs/uniform-crossover.png -------------------------------------------------------------------------------- /Week 3 - Genetic Algorithm(part 2)/genetic_utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from matplotlib.pyplot import pie 3 | import matplotlib.pyplot as plt 4 | from matplotlib import animation 5 | from IPython.display import HTML 6 | 7 | class Item: 8 | def __init__(self, value, weight) : 9 | self.value = value 10 | self.weight = weight 11 | 12 | class Bag: 13 | def __init__(self, capacity) : 14 | self.capacity = capacity 15 | 16 | class Chromosome : 17 | def __init__(self, length) : 18 | self.genes = np.random.rand(length) > .5 19 | self.fitness = float('-inf') 20 | 21 | def __len__(self) : 22 | return len(self.genes) 23 | 24 | def reset(self) : 25 | self.fitness = float('-inf') 26 | 27 | def get_fake_knapsack(seed=0, max_value=30, max_weight=40, items_number=30): 28 | """ 29 | Items & Bag capacity of fake knapsack problem will be generated and returned 30 | """ 31 | np.random.seed(seed) 32 | # items_number = np.random.randint(items_number) 33 | # List Comprehensions, for more details see : https://www.pythonforbeginners.com/basics/list-comprehensions-in-python 34 | items = np.array([Item(np.random.rand()*max_value, np.random.rand()*max_weight) for _ in range(items_number)]) 35 | bag = Bag(np.random.rand()*(max_weight*len(items)) + max_weight) 36 | print('We have {} items with weight and values of :'.format(len(items))) 37 | for i,item in enumerate(items) : 38 | print('item {}: Weight=>{} Value=>{}'.format(i, item.weight, item.value)) 39 | print('We have a bag with capacity of {}'.format(bag.capacity)) 40 | return items, bag 41 | 42 | # Population Initialization Method 43 | population_init = lambda size, chrom_size : np.array([Chromosome(chrom_size) for _ in range(size)]) 44 | 45 | 46 | def fitness_eval(chrom, items, bag, epsilon=2) : 47 | selected_items = items[chrom.genes] 48 | capacity_full = 0 49 | fitness = 0 50 | for item in selected_items : 51 | capacity_full += item.weight 52 | if capacity_full > bag.capacity : 53 | fitness = epsilon 54 | break 55 | fitness += item.value 56 | return fitness 57 | 58 | def roulette_selection(chromosomes, show_plot = False) : 59 | i = 0 60 | fitnesses = np.array(list(map(lambda c: c.fitness, chromosomes))) 61 | sum_of_fitnesses = np.sum(fitnesses) 62 | sel_prob = fitnesses/sum_of_fitnesses 63 | if show_plot: 64 | pie(sel_prob) # Ploting pie chart of probablity of each individual 65 | sum_prob = sel_prob[i] 66 | pointer = np.random.rand() 67 | while sum_prob < pointer : 68 | i += 1 69 | sum_prob += sel_prob[i] 70 | return chromosomes[i] 71 | 72 | tournament_selection = lambda chromosomes, sel_pressure: max(np.random.choice(chromosomes, sel_pressure),key=lambda c: c.fitness) 73 | 74 | # First set up the figure, the axis, and the plot element we want to animate 75 | def plot_generations(generations, fitnesses) : 76 | fig = plt.figure() 77 | ax = plt.axes(xlim=(0, 20), ylim=(0, 20)) 78 | 79 | X = generations[:, :, 0] 80 | Y = generations[:, :, 1] 81 | 82 | # animation function. This is called sequentially 83 | def animate(i): 84 | x = X[i] 85 | y = Y[i] 86 | ax.clear() 87 | scat = ax.scatter(x, y, s=fitnesses[i]) 88 | ax.set_title('Generation {}'.format(i)) 89 | return scat, ax 90 | 91 | # call the animator. blit=True means only re-draw the parts that have changed. 92 | anim = animation.FuncAnimation(fig, animate, frames=generations.shape[0], interval=200) 93 | plt.close() 94 | # call our new function to display the animation 95 | return HTML(anim.to_jshtml()) -------------------------------------------------------------------------------- /Week 4 - Genetic Algorithm(part 3).rar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Computational-Intelligence-Fall18/Computational-Intelligence-Tutorials/18018dd5258ec0563513076c8e46bba8113d65b4/Week 4 - Genetic Algorithm(part 3).rar -------------------------------------------------------------------------------- /Week 4 - Genetic Algorithm(part 3)/None0000000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Computational-Intelligence-Fall18/Computational-Intelligence-Tutorials/18018dd5258ec0563513076c8e46bba8113d65b4/Week 4 - Genetic Algorithm(part 3)/None0000000.png -------------------------------------------------------------------------------- /Week 4 - Genetic Algorithm(part 3)/figs/gray_to_bin.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Computational-Intelligence-Fall18/Computational-Intelligence-Tutorials/18018dd5258ec0563513076c8e46bba8113d65b4/Week 4 - Genetic Algorithm(part 3)/figs/gray_to_bin.PNG -------------------------------------------------------------------------------- /Week 4 - Genetic Algorithm(part 3)/genetic_utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | from copy import deepcopy 5 | from matplotlib.pyplot import pie 6 | from matplotlib import animation 7 | from IPython.display import HTML 8 | 9 | 10 | class Chromosome : 11 | def __init__(self, length) : 12 | self.genes = np.random.rand(length) > .5 13 | self.fitness = float('-inf') 14 | 15 | def __len__(self) : 16 | return len(self.genes) 17 | 18 | def reset(self) : 19 | self.fitness = float('-inf') 20 | 21 | def get_fake_knapsack(seed=0, max_value=30, max_weight=40, items_number=30): 22 | """ 23 | Items & Bag capacity of fake knapsack problem will be generated and returned 24 | """ 25 | np.random.seed(seed) 26 | # items_number = np.random.randint(items_number) 27 | # List Comprehensions, for more details see : https://www.pythonforbeginners.com/basics/list-comprehensions-in-python 28 | items = np.array([Item(np.random.rand()*max_value, np.random.rand()*max_weight) for _ in range(items_number)]) 29 | bag = Bag(np.random.rand()*(max_weight*len(items)) + max_weight) 30 | print('We have {} items with weight and values of :'.format(len(items))) 31 | for i,item in enumerate(items) : 32 | print('item {}: Weight=>{} Value=>{}'.format(i, item.weight, item.value)) 33 | print('We have a bag with capacity of {}'.format(bag.capacity)) 34 | return items, bag 35 | 36 | # Population Initialization Method 37 | population_init = lambda size, chrom_size : np.array([Chromosome(chrom_size) for _ in range(size)]) 38 | 39 | 40 | def fitness_eval(chrom, items, bag, epsilon=2) : 41 | selected_items = items[chrom.genes] 42 | capacity_full = 0 43 | fitness = 0 44 | for item in selected_items : 45 | capacity_full += item.weight 46 | if capacity_full > bag.capacity : 47 | fitness = epsilon 48 | break 49 | fitness += item.value 50 | return fitness 51 | 52 | def roulette_selection(chromosomes, show_plot = False) : 53 | i = 0 54 | fitnesses = np.array(list(map(lambda c: c.fitness, chromosomes))) 55 | sum_of_fitnesses = np.sum(fitnesses) 56 | sel_prob = fitnesses/sum_of_fitnesses 57 | if show_plot: 58 | pie(sel_prob) # Ploting pie chart of probablity of each individual 59 | sum_prob = sel_prob[i] 60 | pointer = np.random.rand() 61 | while sum_prob < pointer : 62 | i += 1 63 | sum_prob += sel_prob[i] 64 | return chromosomes[i] 65 | 66 | tournament_selection = lambda chromosomes, sel_pressure: max(np.random.choice(chromosomes, sel_pressure),key=lambda c: c.fitness) 67 | 68 | def one_point_crossover(pop, selection_method, pc) : 69 | 70 | p1 = selection_method(pop) 71 | p2 = selection_method(pop) 72 | 73 | chrom_length = len(p1) 74 | 75 | point = np.random.randint(1,chrom_length -1) 76 | 77 | if np.random.random() < pc : 78 | c1 = Chromosome(chrom_length) 79 | c2 = Chromosome(chrom_length) 80 | for i in range(chrom_length) : 81 | if i < point : 82 | c1.genes[i] = p1.genes[i] 83 | c2.genes[i] = p2.genes[i] 84 | else : 85 | c1.genes[i] = p2.genes[i] 86 | c2.genes[i] = p1.genes[i] 87 | else : 88 | c1 = deepcopy(p1) 89 | c2 = deepcopy(p2) 90 | 91 | # Reset fitness of each parent 92 | c1.reset() 93 | c2.reset() 94 | 95 | return c1, c2 96 | 97 | def two_point_crossover(pop, selection_method, pc) : 98 | 99 | p1 = selection_method(pop) 100 | p2 = selection_method(pop) 101 | 102 | chrom_length = len(p1) 103 | 104 | point1 = np.random.randint(1,chrom_length -1) 105 | point2 = np.random.randint(1,chrom_length -1) 106 | 107 | if np.random.random() < pc : 108 | c1 = Chromosome(chrom_length) 109 | c2 = Chromosome(chrom_length) 110 | 111 | for i in range(chrom_length) : 112 | if i < point1 : 113 | c1.genes[i] = p1.genes[i] 114 | c2.genes[i] = p2.genes[i] 115 | elif i < point2 : 116 | c1.genes[i] = p2.genes[i] 117 | c2.genes[i] = p1.genes[i] 118 | else : 119 | c1.genes[i] = p1.genes[i] 120 | c2.genes[i] = p2.genes[i] 121 | else : 122 | c1 = deepcopy(p1) 123 | c2 = deepcopy(p2) 124 | 125 | # Reset fitness of each parent 126 | c1.reset() 127 | c2.reset() 128 | 129 | return c1, c2 130 | 131 | 132 | def unifrom_crossover(pop, selection_method, pc) : 133 | p1 = selection_method(pop) 134 | p2 = selection_method(pop) 135 | 136 | chrom_length = len(p1) 137 | 138 | mask = np.random.randint(0, 2, chrom_length) 139 | 140 | if np.random.random() < pc : 141 | c1 = Chromosome(chrom_length) 142 | c2 = Chromosome(chrom_length) 143 | for i in chrom_length : 144 | if mask[i]: 145 | c1.genes[i] = p2.genes[i] 146 | c2.genes[i] = p1.genes[i] 147 | else : 148 | c1.genes[i] = p1.genes[i] 149 | c2.genes[i] = p2.genes[i] 150 | else : 151 | c1 = deepcopy(p1) 152 | c2 = deepcopy(p2) 153 | 154 | # Reset fitness of each parent 155 | c1.reset() 156 | c2.reset() 157 | 158 | return c1, c2 159 | 160 | def random_mutation(chrom, pm) : 161 | for i in range(len(chrom)): 162 | if np.random.random() < pm : 163 | chrom.genes[i] = not chrom.genes[i] 164 | return chrom 165 | 166 | def random_mutation_v2(chrom, pm) : 167 | if np.random.random() < pm : 168 | point = np.random.randint(len(chrom)) 169 | chrom.genes[point] = not chrom.genes[point] 170 | return chrom 171 | 172 | def inorder_mutation(chrom, pm, mutation_points) : 173 | for i in mutation_points: 174 | if np.random.random() < pm : 175 | chrom.genes[i] = not chrom.genes[i] 176 | return chrom 177 | 178 | # First set up the figure, the axis, and the plot element we want to animate 179 | def plot_generations(generations, fitnesses) : 180 | fig = plt.figure() 181 | ax = plt.axes(xlim=(0, 20), ylim=(0, 20)) 182 | 183 | X = generations[:, :, 0] 184 | Y = generations[:, :, 1] 185 | 186 | # animation function. This is called sequentially 187 | def animate(i): 188 | x = X[i] 189 | y = Y[i] 190 | ax.clear() 191 | scat = ax.scatter(x, y, s=fitnesses[i]) 192 | ax.set_title('Generation {}'.format(i)) 193 | return scat, ax 194 | 195 | # call the animator. blit=True means only re-draw the parts that have changed. 196 | anim = animation.FuncAnimation(fig, animate, frames=generations.shape[0], interval=200) 197 | plt.close() 198 | # call our new function to display the animation 199 | return HTML(anim.to_jshtml()) -------------------------------------------------------------------------------- /Week 5 - Ant Colony Optimization/Ant Colony.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Before we begin" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "* Github (Github Education)\n", 15 | "* Bitbucket\n", 16 | "* Kaggle" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": { 22 | "slideshow": { 23 | "slide_type": "slide" 24 | } 25 | }, 26 | "source": [ 27 | "# Introduction\n", 28 | "In this week, we want to implement an Ant Colony Optimization Algorithm to solve Travelling Sales Man problem." 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 1, 34 | "metadata": {}, 35 | "outputs": [], 36 | "source": [ 37 | "import random\n", 38 | "import math\n", 39 | "import operator\n", 40 | "import matplotlib.pyplot as plt" 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "metadata": {}, 46 | "source": [ 47 | "# Content\n", 48 | "* Travelling Sales Man Problem\n", 49 | "* Helper Functions\n", 50 | "* Cost & Pheromone Graph\n", 51 | "* Designing Ants\n", 52 | "* Designing ACO\n", 53 | "* Running" 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "metadata": {}, 59 | "source": [ 60 | "# Travelling Sales Man Problem(TSP)" 61 | ] 62 | }, 63 | { 64 | "cell_type": "markdown", 65 | "metadata": {}, 66 | "source": [ 67 | "The travelling salesman problem (TSP) asks the following question: \"Given a list of cities and the distances between each pair of cities, what is the shortest possible route that visits each city and returns to the origin city?\" It is an NP-hard problem in combinatorial optimization, important in operations research and theoretical computer science." 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "metadata": {}, 73 | "source": [ 74 | "![TSP Sample](figs/tsp.png)" 75 | ] 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "metadata": {}, 80 | "source": [ 81 | "# Helper Functions" 82 | ] 83 | }, 84 | { 85 | "cell_type": "markdown", 86 | "metadata": {}, 87 | "source": [ 88 | "\\begin{align}\n", 89 | "Distance = \\sqrt{ (y_2 - y_1)^2 + (x_2 - x_1)^2}\n", 90 | "\\end{align}" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": 2, 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [ 99 | "def distance(city1: dict, city2: dict):\n", 100 | " return math.sqrt((city1['x'] - city2['x']) ** 2 + (city1['y'] - city2['y']) ** 2)" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": 3, 106 | "metadata": {}, 107 | "outputs": [], 108 | "source": [ 109 | "def plot(points, path: list):\n", 110 | " x = []\n", 111 | " y = []\n", 112 | " for point in points:\n", 113 | " x.append(point[0])\n", 114 | " y.append(point[1])\n", 115 | " \n", 116 | " y = list(map(operator.sub, [max(y) for i in range(len(points))], y)) # for better visualization\n", 117 | " plt.plot(x, y, 'co')\n", 118 | "\n", 119 | " for k in range(1, len(path)):\n", 120 | " i = path[k - 1] # index of first city\n", 121 | " j = path[k] # index of next city\n", 122 | " \n", 123 | " plt.arrow(x[i], y[i], x[j] - x[i], y[j] - y[i], color='r', length_includes_head=True)\n", 124 | "\n", 125 | " \n", 126 | " plt.xlim(0, max(x) * 1.1)\n", 127 | " \n", 128 | " plt.ylim(0, max(y) * 1.1)\n", 129 | " \n", 130 | " plt.show()" 131 | ] 132 | }, 133 | { 134 | "cell_type": "markdown", 135 | "metadata": {}, 136 | "source": [ 137 | "# Prerequisites" 138 | ] 139 | }, 140 | { 141 | "cell_type": "markdown", 142 | "metadata": {}, 143 | "source": [ 144 | "### ACO Algorihtm\n", 145 | "![ACO Algorithm](figs/aco_algorithm.PNG)\n", 146 | "### Strategy\n", 147 | "![Q Updating](figs/strategy.PNG)\n", 148 | "With $Q \\in [0,1]$\n", 149 | "### Rho\n", 150 | "\\begin{align}\n", 151 | "T_{ij}(t) \\leftarrow rho * T_{ij}(t)\n", 152 | "\\end{align}\n", 153 | "With $rho \\in [0,1]$\n", 154 | "### Transition Probability\n", 155 | "![Transition Probability](figs/transition_prob.PNG)\n", 156 | "With $\\alpha, \\beta \\in [0,1]$" 157 | ] 158 | }, 159 | { 160 | "cell_type": "markdown", 161 | "metadata": {}, 162 | "source": [ 163 | "# Cost & Pheromone Graph" 164 | ] 165 | }, 166 | { 167 | "cell_type": "markdown", 168 | "metadata": {}, 169 | "source": [ 170 | "The graph is a data structure that has the matrices that we needed to evaluate transition probability:" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": 4, 176 | "metadata": {}, 177 | "outputs": [], 178 | "source": [ 179 | "class Graph(object):\n", 180 | " def __init__(self, cost_matrix: list, rank: int):\n", 181 | " \"\"\"\n", 182 | " :param cost_matrix:\n", 183 | " :param rank: rank of the cost matrix\n", 184 | " \"\"\"\n", 185 | " self.matrix = cost_matrix\n", 186 | " self.rank = rank\n", 187 | " # noinspection PyUnusedLocal\n", 188 | " self.pheromone = [[1 / (rank * rank) for j in range(rank)] for i in range(rank)]" 189 | ] 190 | }, 191 | { 192 | "cell_type": "markdown", 193 | "metadata": {}, 194 | "source": [ 195 | "# Designing Ants" 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": 5, 201 | "metadata": {}, 202 | "outputs": [], 203 | "source": [ 204 | "class Ant(object):\n", 205 | " def __init__(self, aco, graph: Graph):\n", 206 | " self.colony = aco\n", 207 | " self.graph = graph\n", 208 | " self.total_cost = 0.0\n", 209 | " self.path = [] # path\n", 210 | " self.pheromone_delta = [] # the local increase of pheromone\n", 211 | " self.allowed = [i for i in range(graph.rank)] # nodes which are allowed for the next selection\n", 212 | " self.eta = [[0 if i == j else 1 / graph.matrix[i][j] for j in range(graph.rank)] \\\n", 213 | " for i in range(graph.rank)] # heuristic information for calculating \n", 214 | " start = random.randint(0, graph.rank - 1) # start from any node\n", 215 | " self.path.append(start)\n", 216 | " self.current = start\n", 217 | " self.allowed.remove(start)\n", 218 | "\n", 219 | " def select_next(self):\n", 220 | " denominator = 0\n", 221 | " for i in self.allowed:\n", 222 | " denominator += self.graph.pheromone[self.current][i] ** self.colony.alpha \\\n", 223 | " * self.eta[self.current][i] ** self.colony.beta\n", 224 | " # noinspection PyUnusedLocal\n", 225 | " probabilities = [0 for i in range(self.graph.rank)] # probabilities for moving to a node in the next step\n", 226 | " for i in range(self.graph.rank):\n", 227 | " try:\n", 228 | " self.allowed.index(i) # test if allowed list contains i\n", 229 | " probabilities[i] = self.graph.pheromone[self.current][i] ** self.colony.alpha * \\\n", 230 | " self.eta[self.current][i] ** self.colony.beta / denominator\n", 231 | " except ValueError:\n", 232 | " pass # do nothing\n", 233 | " \n", 234 | " # select next node by probability roulette\n", 235 | " selected = 0\n", 236 | " rand = random.random()\n", 237 | " for i, probability in enumerate(probabilities):\n", 238 | " rand -= probability\n", 239 | " if rand <= 0:\n", 240 | " selected = i\n", 241 | " break\n", 242 | " self.allowed.remove(selected)\n", 243 | " self.path.append(selected)\n", 244 | " self.total_cost += self.graph.matrix[self.current][selected]\n", 245 | " self.current = selected\n", 246 | "\n", 247 | " # noinspection PyUnusedLocal\n", 248 | " def update_pheromone_delta(self):\n", 249 | " self.pheromone_delta = [[0 for j in range(self.graph.rank)] for i in range(self.graph.rank)]\n", 250 | " for k in range(1, len(self.path)):\n", 251 | " i = self.path[k - 1]\n", 252 | " j = self.path[k]\n", 253 | " if self.colony.update_strategy == 1: # ant-quality system\n", 254 | " self.pheromone_delta[i][j] = self.colony.Q\n", 255 | " elif self.colony.update_strategy == 2: # ant-density system\n", 256 | " # noinspection PyTypeChecker\n", 257 | " self.pheromone_delta[i][j] = self.colony.Q / self.graph.matrix[i][j]\n", 258 | " else: # ant-cycle system\n", 259 | " self.pheromone_delta[i][j] = self.colony.Q / self.total_cost" 260 | ] 261 | }, 262 | { 263 | "cell_type": "markdown", 264 | "metadata": {}, 265 | "source": [ 266 | "# Designing ACO" 267 | ] 268 | }, 269 | { 270 | "cell_type": "code", 271 | "execution_count": 6, 272 | "metadata": {}, 273 | "outputs": [], 274 | "source": [ 275 | "class ACO(object):\n", 276 | " def __init__(self, ant_count: int, generations: int,\n", 277 | " alpha: float, beta: float, rho: float,\n", 278 | " q: int, strategy: int):\n", 279 | " \"\"\"\n", 280 | " :param ant_count:\n", 281 | " :param generations:\n", 282 | " :param alpha: relative importance of pheromone\n", 283 | " :param beta: relative importance of heuristic information\n", 284 | " :param rho: pheromone residual coefficient\n", 285 | " :param q: pheromone intensity\n", 286 | " :param strategy: pheromone update strategy. 0 - ant-cycle, 1 - ant-quality, 2 - ant-density\n", 287 | " \"\"\"\n", 288 | " self.Q = q\n", 289 | " self.rho = rho # Evapuration Rate\n", 290 | " self.beta = beta\n", 291 | " self.alpha = alpha\n", 292 | " self.ant_count = ant_count\n", 293 | " self.generations = generations\n", 294 | " self.update_strategy = strategy\n", 295 | "\n", 296 | " def _update_pheromone(self, graph: Graph, ants: list):\n", 297 | " for i, row in enumerate(graph.pheromone):\n", 298 | " for j, col in enumerate(row):\n", 299 | " graph.pheromone[i][j] *= self.rho # Evapuration\n", 300 | " for ant in ants:\n", 301 | " graph.pheromone[i][j] += ant.pheromone_delta[i][j]\n", 302 | "\n", 303 | " def solve(self, graph: Graph):\n", 304 | " \"\"\"\n", 305 | " :param graph:\n", 306 | " \"\"\"\n", 307 | " best_cost = float('inf')\n", 308 | " best_solution = []\n", 309 | " for gen in range(self.generations):\n", 310 | " # noinspection PyUnusedLocal\n", 311 | " ants = [Ant(self, graph) for i in range(self.ant_count)]\n", 312 | " for ant in ants:\n", 313 | " for i in range(graph.rank - 1):\n", 314 | " ant.select_next()\n", 315 | " ant.total_cost += graph.matrix[ant.path[-1]][ant.path[0]]\n", 316 | " if ant.total_cost < best_cost:\n", 317 | " best_cost = ant.total_cost\n", 318 | " best_solution = [] + ant.path\n", 319 | " # update pheromone\n", 320 | " ant.update_pheromone_delta()\n", 321 | " self._update_pheromone(graph, ants)\n", 322 | " print('generation #{}, best cost: {}, path: {}'.format(gen, best_cost, best_solution))\n", 323 | " return best_solution, best_cost" 324 | ] 325 | }, 326 | { 327 | "cell_type": "markdown", 328 | "metadata": {}, 329 | "source": [ 330 | "# Running" 331 | ] 332 | }, 333 | { 334 | "cell_type": "code", 335 | "execution_count": 7, 336 | "metadata": {}, 337 | "outputs": [ 338 | { 339 | "name": "stdout", 340 | "output_type": "stream", 341 | "text": [ 342 | "generation #0, best cost: 17120.042816344583, path: [9, 8, 7, 1, 3, 4, 5, 6, 12, 11, 13, 10, 22, 15, 16, 18, 23, 24, 19, 20, 21, 17, 2, 25, 27, 26, 30, 29, 28, 0, 14]\n", 343 | "generation #1, best cost: 17120.042816344583, path: [9, 8, 7, 1, 3, 4, 5, 6, 12, 11, 13, 10, 22, 15, 16, 18, 23, 24, 19, 20, 21, 17, 2, 25, 27, 26, 30, 29, 28, 0, 14]\n", 344 | "generation #2, best cost: 17120.042816344583, path: [9, 8, 7, 1, 3, 4, 5, 6, 12, 11, 13, 10, 22, 15, 16, 18, 23, 24, 19, 20, 21, 17, 2, 25, 27, 26, 30, 29, 28, 0, 14]\n", 345 | "generation #3, best cost: 17120.042816344583, path: [9, 8, 7, 1, 3, 4, 5, 6, 12, 11, 13, 10, 22, 15, 16, 18, 23, 24, 19, 20, 21, 17, 2, 25, 27, 26, 30, 29, 28, 0, 14]\n", 346 | "generation #4, best cost: 17120.042816344583, path: [9, 8, 7, 1, 3, 4, 5, 6, 12, 11, 13, 10, 22, 15, 16, 18, 23, 24, 19, 20, 21, 17, 2, 25, 27, 26, 30, 29, 28, 0, 14]\n", 347 | "generation #5, best cost: 17120.042816344583, path: [9, 8, 7, 1, 3, 4, 5, 6, 12, 11, 13, 10, 22, 15, 16, 18, 23, 24, 19, 20, 21, 17, 2, 25, 27, 26, 30, 29, 28, 0, 14]\n", 348 | "generation #6, best cost: 17120.042816344583, path: [9, 8, 7, 1, 3, 4, 5, 6, 12, 11, 13, 10, 22, 15, 16, 18, 23, 24, 19, 20, 21, 17, 2, 25, 27, 26, 30, 29, 28, 0, 14]\n", 349 | "generation #7, best cost: 17120.042816344583, path: [9, 8, 7, 1, 3, 4, 5, 6, 12, 11, 13, 10, 22, 15, 16, 18, 23, 24, 19, 20, 21, 17, 2, 25, 27, 26, 30, 29, 28, 0, 14]\n", 350 | "generation #8, best cost: 17120.042816344583, path: [9, 8, 7, 1, 3, 4, 5, 6, 12, 11, 13, 10, 22, 15, 16, 18, 23, 24, 19, 20, 21, 17, 2, 25, 27, 26, 30, 29, 28, 0, 14]\n", 351 | "generation #9, best cost: 17120.042816344583, path: [9, 8, 7, 1, 3, 4, 5, 6, 12, 11, 13, 10, 22, 15, 16, 18, 23, 24, 19, 20, 21, 17, 2, 25, 27, 26, 30, 29, 28, 0, 14]\n", 352 | "generation #10, best cost: 17120.042816344583, path: [9, 8, 7, 1, 3, 4, 5, 6, 12, 11, 13, 10, 22, 15, 16, 18, 23, 24, 19, 20, 21, 17, 2, 25, 27, 26, 30, 29, 28, 0, 14]\n", 353 | "generation #11, best cost: 17120.042816344583, path: [9, 8, 7, 1, 3, 4, 5, 6, 12, 11, 13, 10, 22, 15, 16, 18, 23, 24, 19, 20, 21, 17, 2, 25, 27, 26, 30, 29, 28, 0, 14]\n", 354 | "generation #12, best cost: 17120.042816344583, path: [9, 8, 7, 1, 3, 4, 5, 6, 12, 11, 13, 10, 22, 15, 16, 18, 23, 24, 19, 20, 21, 17, 2, 25, 27, 26, 30, 29, 28, 0, 14]\n", 355 | "generation #13, best cost: 17120.042816344583, path: [9, 8, 7, 1, 3, 4, 5, 6, 12, 11, 13, 10, 22, 15, 16, 18, 23, 24, 19, 20, 21, 17, 2, 25, 27, 26, 30, 29, 28, 0, 14]\n", 356 | "generation #14, best cost: 17120.042816344583, path: [9, 8, 7, 1, 3, 4, 5, 6, 12, 11, 13, 10, 22, 15, 16, 18, 23, 24, 19, 20, 21, 17, 2, 25, 27, 26, 30, 29, 28, 0, 14]\n", 357 | "generation #15, best cost: 17120.042816344583, path: [9, 8, 7, 1, 3, 4, 5, 6, 12, 11, 13, 10, 22, 15, 16, 18, 23, 24, 19, 20, 21, 17, 2, 25, 27, 26, 30, 29, 28, 0, 14]\n", 358 | "generation #16, best cost: 17120.042816344583, path: [9, 8, 7, 1, 3, 4, 5, 6, 12, 11, 13, 10, 22, 15, 16, 18, 23, 24, 19, 20, 21, 17, 2, 25, 27, 26, 30, 29, 28, 0, 14]\n", 359 | "generation #17, best cost: 17120.042816344583, path: [9, 8, 7, 1, 3, 4, 5, 6, 12, 11, 13, 10, 22, 15, 16, 18, 23, 24, 19, 20, 21, 17, 2, 25, 27, 26, 30, 29, 28, 0, 14]\n", 360 | "generation #18, best cost: 17120.042816344583, path: [9, 8, 7, 1, 3, 4, 5, 6, 12, 11, 13, 10, 22, 15, 16, 18, 23, 24, 19, 20, 21, 17, 2, 25, 27, 26, 30, 29, 28, 0, 14]\n", 361 | "generation #19, best cost: 17120.042816344583, path: [9, 8, 7, 1, 3, 4, 5, 6, 12, 11, 13, 10, 22, 15, 16, 18, 23, 24, 19, 20, 21, 17, 2, 25, 27, 26, 30, 29, 28, 0, 14]\n", 362 | "generation #20, best cost: 17120.042816344583, path: [9, 8, 7, 1, 3, 4, 5, 6, 12, 11, 13, 10, 22, 15, 16, 18, 23, 24, 19, 20, 21, 17, 2, 25, 27, 26, 30, 29, 28, 0, 14]\n", 363 | "generation #21, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 364 | "generation #22, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 365 | "generation #23, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 366 | "generation #24, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 367 | "generation #25, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 368 | "generation #26, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 369 | "generation #27, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 370 | "generation #28, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 371 | "generation #29, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 372 | "generation #30, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 373 | "generation #31, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 374 | "generation #32, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 375 | "generation #33, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 376 | "generation #34, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 377 | "generation #35, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 378 | "generation #36, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 379 | "generation #37, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 380 | "generation #38, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 381 | "generation #39, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 382 | "generation #40, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 383 | "generation #41, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 384 | "generation #42, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 385 | "generation #43, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 386 | "generation #44, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 387 | "generation #45, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 388 | "generation #46, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 389 | "generation #47, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 390 | "generation #48, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n" 391 | ] 392 | }, 393 | { 394 | "name": "stdout", 395 | "output_type": "stream", 396 | "text": [ 397 | "generation #49, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 398 | "generation #50, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 399 | "generation #51, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 400 | "generation #52, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 401 | "generation #53, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 402 | "generation #54, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 403 | "generation #55, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 404 | "generation #56, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 405 | "generation #57, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 406 | "generation #58, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 407 | "generation #59, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 408 | "generation #60, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 409 | "generation #61, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 410 | "generation #62, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 411 | "generation #63, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 412 | "generation #64, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 413 | "generation #65, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 414 | "generation #66, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 415 | "generation #67, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 416 | "generation #68, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 417 | "generation #69, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 418 | "generation #70, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 419 | "generation #71, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 420 | "generation #72, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 421 | "generation #73, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 422 | "generation #74, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 423 | "generation #75, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 424 | "generation #76, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 425 | "generation #77, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 426 | "generation #78, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 427 | "generation #79, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 428 | "generation #80, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 429 | "generation #81, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 430 | "generation #82, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 431 | "generation #83, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 432 | "generation #84, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 433 | "generation #85, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 434 | "generation #86, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 435 | "generation #87, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 436 | "generation #88, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 437 | "generation #89, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 438 | "generation #90, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 439 | "generation #91, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 440 | "generation #92, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 441 | "generation #93, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 442 | "generation #94, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 443 | "generation #95, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 444 | "generation #96, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 445 | "generation #97, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n" 446 | ] 447 | }, 448 | { 449 | "name": "stdout", 450 | "output_type": "stream", 451 | "text": [ 452 | "generation #98, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 453 | "generation #99, best cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n", 454 | "cost: 16970.978236089806, path: [30, 29, 26, 27, 25, 20, 21, 17, 2, 16, 18, 23, 19, 24, 10, 11, 13, 12, 6, 5, 4, 3, 1, 15, 22, 7, 8, 9, 14, 0, 28]\n" 455 | ] 456 | }, 457 | { 458 | "data": { 459 | "image/png": "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\n", 460 | "text/plain": [ 461 | "" 462 | ] 463 | }, 464 | "metadata": {}, 465 | "output_type": "display_data" 466 | } 467 | ], 468 | "source": [ 469 | "def main():\n", 470 | " # Loading Data from files\n", 471 | " cities = []\n", 472 | " points = []\n", 473 | " with open('./data/chn31.txt') as f:\n", 474 | " for line in f.readlines():\n", 475 | " city = line.split(' ')\n", 476 | " cities.append(dict(index=int(city[0]), x=int(city[1]), y=int(city[2])))\n", 477 | " points.append((int(city[1]), int(city[2])))\n", 478 | " \n", 479 | " # Calculating Cost matrix => distance between city i and j\n", 480 | " cost_matrix = []\n", 481 | " rank = len(cities)\n", 482 | " for i in range(rank):\n", 483 | " row = []\n", 484 | " for j in range(rank):\n", 485 | " row.append(distance(cities[i], cities[j]))\n", 486 | " cost_matrix.append(row)\n", 487 | " \n", 488 | " # Instaniate ACO, and Run\n", 489 | " aco = ACO(10, 100, 1.0, 10.0, 0.5, 10, 2)\n", 490 | " graph = Graph(cost_matrix, rank)\n", 491 | " path, cost = aco.solve(graph)\n", 492 | " print('cost: {}, path: {}'.format(cost, path))\n", 493 | " \n", 494 | " # Ploting the best cycle found\n", 495 | " plot(points, path)\n", 496 | "\n", 497 | "if __name__ == '__main__':\n", 498 | " main()" 499 | ] 500 | } 501 | ], 502 | "metadata": { 503 | "kernelspec": { 504 | "display_name": "Python 3", 505 | "language": "python", 506 | "name": "python3" 507 | }, 508 | "language_info": { 509 | "codemirror_mode": { 510 | "name": "ipython", 511 | "version": 3 512 | }, 513 | "file_extension": ".py", 514 | "mimetype": "text/x-python", 515 | "name": "python", 516 | "nbconvert_exporter": "python", 517 | "pygments_lexer": "ipython3", 518 | "version": "3.6.4" 519 | } 520 | }, 521 | "nbformat": 4, 522 | "nbformat_minor": 2 523 | } 524 | -------------------------------------------------------------------------------- /Week 5 - Ant Colony Optimization/data/att48.txt: -------------------------------------------------------------------------------- 1 | 1 6734 1453 2 | 2 2233 10 3 | 3 5530 1424 4 | 4 401 841 5 | 5 3082 1644 6 | 6 7608 4458 7 | 7 7573 3716 8 | 8 7265 1268 9 | 9 6898 1885 10 | 10 1112 2049 11 | 11 5468 2606 12 | 12 5989 2873 13 | 13 4706 2674 14 | 14 4612 2035 15 | 15 6347 2683 16 | 16 6107 669 17 | 17 7611 5184 18 | 18 7462 3590 19 | 19 7732 4723 20 | 20 5900 3561 21 | 21 4483 3369 22 | 22 6101 1110 23 | 23 5199 2182 24 | 24 1633 2809 25 | 25 4307 2322 26 | 26 675 1006 27 | 27 7555 4819 28 | 28 7541 3981 29 | 29 3177 756 30 | 30 7352 4506 31 | 31 7545 2801 32 | 32 3245 3305 33 | 33 6426 3173 34 | 34 4608 1198 35 | 35 23 2216 36 | 36 7248 3779 37 | 37 7762 4595 38 | 38 7392 2244 39 | 39 3484 2829 40 | 40 6271 2135 41 | 41 4985 140 42 | 42 1916 1569 43 | 43 7280 4899 44 | 44 7509 3239 45 | 45 10 2676 46 | 46 6807 2993 47 | 47 5185 3258 48 | 48 3023 1942 -------------------------------------------------------------------------------- /Week 5 - Ant Colony Optimization/data/chn144.txt: -------------------------------------------------------------------------------- 1 | 1 3639 1315 2 | 2 3569 1438 3 | 3 3904 1289 4 | 4 3506 1221 5 | 5 3237 1764 6 | 6 3089 1251 7 | 7 3238 1229 8 | 8 4172 1125 9 | 9 4020 1142 10 | 10 4095 626 11 | 11 4403 1022 12 | 12 4361 73 13 | 13 4634 654 14 | 14 2846 1951 15 | 15 3054 1710 16 | 16 2562 1756 17 | 17 2291 1403 18 | 18 2012 1552 19 | 19 682 825 20 | 20 518 1251 21 | 21 1332 695 22 | 22 4016 1715 23 | 23 4087 1546 24 | 24 4062 2220 25 | 25 4061 2370 26 | 26 4201 2397 27 | 27 3777 2095 28 | 28 3888 2261 29 | 29 3678 2463 30 | 30 3789 2620 31 | 31 3862 2839 32 | 32 4263 2931 33 | 33 3492 1901 34 | 34 3479 2198 35 | 35 3318 2408 36 | 36 3296 2217 37 | 37 3394 2643 38 | 38 3101 2721 39 | 39 3792 3156 40 | 40 3143 3421 41 | 41 3130 2973 42 | 42 2765 3321 43 | 43 2545 2357 44 | 44 2611 2275 45 | 45 2860 2862 46 | 46 2801 2700 47 | 47 2370 2975 48 | 48 1084 2313 49 | 49 4177 2244 50 | 50 3757 1187 51 | 51 3488 1535 52 | 52 3374 1750 53 | 53 3326 1556 54 | 54 3258 911 55 | 55 3646 234 56 | 56 4089 1387 57 | 57 4196 1044 58 | 58 4312 790 59 | 59 4685 830 60 | 60 4720 557 61 | 61 4153 426 62 | 62 2831 2099 63 | 63 3086 1516 64 | 64 2716 1924 65 | 65 2751 1559 66 | 66 1779 1626 67 | 67 1478 267 68 | 68 278 890 69 | 69 3715 1678 70 | 70 4181 1574 71 | 71 3929 1892 72 | 72 3751 1945 73 | 73 4207 2533 74 | 74 4139 2615 75 | 75 3780 2212 76 | 76 3594 2900 77 | 77 3676 2578 78 | 78 4029 2838 79 | 79 3928 3029 80 | 80 4186 3037 81 | 81 3322 1916 82 | 82 3429 1908 83 | 83 3176 2150 84 | 84 3229 2367 85 | 85 3402 2912 86 | 86 3402 2510 87 | 87 3468 3018 88 | 88 3356 3212 89 | 89 3044 3081 90 | 90 3140 3550 91 | 91 2769 2492 92 | 92 2348 2652 93 | 93 2778 2826 94 | 94 2126 2896 95 | 95 1890 3033 96 | 96 3538 3298 97 | 97 3712 1399 98 | 98 3493 1696 99 | 99 3791 1339 100 | 100 3376 1306 101 | 101 3188 1881 102 | 102 3814 261 103 | 103 3583 864 104 | 104 4297 1218 105 | 105 4116 1187 106 | 106 4252 882 107 | 107 4386 570 108 | 108 4643 404 109 | 109 4784 279 110 | 110 3007 1970 111 | 111 1828 1210 112 | 112 2061 1277 113 | 113 2788 1491 114 | 114 2381 1676 115 | 115 1777 892 116 | 116 1064 284 117 | 117 3688 1818 118 | 118 3896 1656 119 | 119 3918 2179 120 | 120 3972 2136 121 | 121 4029 2498 122 | 122 3766 2364 123 | 123 3896 2443 124 | 124 3796 2499 125 | 125 3478 2705 126 | 126 3810 2969 127 | 127 4167 3206 128 | 128 3486 1755 129 | 129 3334 2107 130 | 130 3587 2417 131 | 131 3507 2376 132 | 132 3264 2551 133 | 133 3360 2792 134 | 134 3439 3201 135 | 135 3526 3263 136 | 136 3012 3394 137 | 137 2935 3240 138 | 138 3053 3739 139 | 139 2284 2803 140 | 140 2577 2574 141 | 141 2592 2820 142 | 142 2401 3164 143 | 143 1304 2312 144 | 144 3470 3304 145 | -------------------------------------------------------------------------------- /Week 5 - Ant Colony Optimization/data/chn31.txt: -------------------------------------------------------------------------------- 1 | 1 1304 2312 2 | 2 3639 1315 3 | 3 4177 2244 4 | 4 3712 1399 5 | 5 3488 1535 6 | 6 3326 1556 7 | 7 3238 1229 8 | 8 4196 1004 9 | 9 4312 790 10 | 10 4386 570 11 | 11 3007 1970 12 | 12 2562 1756 13 | 13 2788 1491 14 | 14 2381 1676 15 | 15 1332 695 16 | 16 3715 1678 17 | 17 3918 2179 18 | 18 4061 2370 19 | 19 3780 2212 20 | 20 3676 2578 21 | 21 4029 2838 22 | 22 4263 2931 23 | 23 3429 1908 24 | 24 3507 2367 25 | 25 3394 2643 26 | 26 3439 3201 27 | 27 2935 3240 28 | 28 3140 3550 29 | 29 2545 2357 30 | 30 2778 2826 31 | 31 2370 2975 32 | -------------------------------------------------------------------------------- /Week 5 - Ant Colony Optimization/figs/aco_algorithm.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Computational-Intelligence-Fall18/Computational-Intelligence-Tutorials/18018dd5258ec0563513076c8e46bba8113d65b4/Week 5 - Ant Colony Optimization/figs/aco_algorithm.PNG -------------------------------------------------------------------------------- /Week 5 - Ant Colony Optimization/figs/strategy.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Computational-Intelligence-Fall18/Computational-Intelligence-Tutorials/18018dd5258ec0563513076c8e46bba8113d65b4/Week 5 - Ant Colony Optimization/figs/strategy.PNG -------------------------------------------------------------------------------- /Week 5 - Ant Colony Optimization/figs/transition_prob.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Computational-Intelligence-Fall18/Computational-Intelligence-Tutorials/18018dd5258ec0563513076c8e46bba8113d65b4/Week 5 - Ant Colony Optimization/figs/transition_prob.PNG -------------------------------------------------------------------------------- /Week 5 - Ant Colony Optimization/figs/tsp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Computational-Intelligence-Fall18/Computational-Intelligence-Tutorials/18018dd5258ec0563513076c8e46bba8113d65b4/Week 5 - Ant Colony Optimization/figs/tsp.png -------------------------------------------------------------------------------- /Week 6 - Particle Swarm Optimization/None0000000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Computational-Intelligence-Fall18/Computational-Intelligence-Tutorials/18018dd5258ec0563513076c8e46bba8113d65b4/Week 6 - Particle Swarm Optimization/None0000000.png -------------------------------------------------------------------------------- /Week 6 - Particle Swarm Optimization/pso_utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from matplotlib import cm 4 | from mpl_toolkits.mplot3d import Axes3D 5 | from matplotlib import animation 6 | from IPython.display import HTML 7 | 8 | def data_point_creator(x_bound, y_bound, func, prec=.1): 9 | x = np.arange(-5.12, 5.12, prec) 10 | y = np.arange(-5.12, 5.12, prec) 11 | x, y = np.meshgrid(x, y) 12 | z = np.array(list(map(func, x, y))) 13 | return x, y, z 14 | 15 | def three_d_plot(x, y, z, p_type='surface', genetic_points=None, with_countour=False, elev=45): 16 | fig = plt.figure() 17 | ax = fig.gca(projection='3d') 18 | 19 | 20 | plot_dict = { 21 | 'surface': ax.plot_surface, 22 | 'wireframe': ax.plot_wireframe, 23 | } 24 | 25 | assert p_type in plot_dict.keys() 26 | 27 | def animate(i): 28 | x_gen = genetic_points[i, :, 0] 29 | y_gen = genetic_points[i, :, 1] 30 | z_gen = genetic_points[i, :, 2] 31 | ax.clear() 32 | ax.scatter(x_gen, y_gen, z_gen, c='black',s=30) 33 | plot_dict[p_type](x, y, z) 34 | ax.contour(x, y, z, zdir='z', offset=-2, cmap=cm.coolwarm) 35 | ax.set_title('Generation {}'.format(i)) 36 | ax.set_xlabel('X') 37 | ax.set_xlim(-10, 10) 38 | ax.set_ylabel('Y') 39 | ax.set_ylim(-10, 10) 40 | ax.set_zlabel('Z') 41 | ax.set_zlim(-2.2, 100) 42 | ax.view_init(elev=elev) 43 | 44 | 45 | return ax 46 | 47 | plot_dict[p_type](x, y, z) 48 | 49 | if with_countour : 50 | # cset = ax.contour(x, y, z, zdir='z', offset=-25, cmap=cm.coolwarm) 51 | # cset = ax.contour(x, y, z, zdir='x', offset=-10, cmap=cm.coolwarm) 52 | cset = ax.contour(x, y, z, zdir='y', offset=10, cmap=cm.coolwarm) 53 | ax.set_xlabel('X') 54 | ax.set_xlim(-10, 10) 55 | ax.set_ylabel('Y') 56 | ax.set_ylim(-10, 10) 57 | ax.set_zlabel('Z') 58 | ax.set_zlim(-25, 25) 59 | if not(genetic_points is None) : 60 | anim = animation.FuncAnimation(fig, animate, frames=genetic_points.shape[0], interval=200) 61 | plt.close() 62 | # call our new function to display the animation 63 | return HTML(anim.to_jshtml()) 64 | 65 | def two_d_plot(x, y, z, genetic_points=None, with_countour=False, elev=45): 66 | fig = plt.figure() 67 | ax = fig.gca() 68 | 69 | def animate(i): 70 | x_gen = genetic_points[i, :, 0] 71 | y_gen = genetic_points[i, :, 1] 72 | ax.clear() 73 | ax.scatter(x_gen, y_gen, c='black',s=30) 74 | ax.contour(x, y, z, zdir='z', offset=-2, cmap=cm.coolwarm) 75 | ax.set_title('Generation {}'.format(i)) 76 | ax.set_xlabel('X') 77 | ax.set_xlim(-10, 10) 78 | ax.set_ylabel('Y') 79 | ax.set_ylim(-10, 10) 80 | return ax 81 | 82 | 83 | if with_countour : 84 | cset = ax.contour(x, y, z, zdir='y', offset=10, cmap=cm.coolwarm) 85 | ax.set_xlabel('X') 86 | ax.set_xlim(-10, 10) 87 | ax.set_ylabel('Y') 88 | ax.set_ylim(-10, 10) 89 | if not(genetic_points is None) : 90 | anim = animation.FuncAnimation(fig, animate, frames=genetic_points.shape[0], interval=200) 91 | plt.close() 92 | # call our new function to display the animation 93 | return HTML(anim.to_jshtml()) 94 | 95 | de_jong_func = lambda x, y: x**2 + y**2 96 | a_p_hyper_ellipsoid_func = lambda x, y: x**2 + 2*y**2 97 | ros_valley_func = lambda x, y: 100*(y - x**2)**2 + (1 -x)**2 98 | rastrigin_func = lambda x, y: 20 + np.floor(x**2 + 10*np.cos(2*np.pi*x)) + np.floor(y**2 + 10*np.cos(2*np.pi*y)) 99 | multi_rastrigin_func = lambda x: 10*len(x) + sum([np.floor(i**2 + 10*np.cos(2*np.pi*i)) for i in x]) -------------------------------------------------------------------------------- /Week 7 - Logistic Regression/data/Iris.csv: -------------------------------------------------------------------------------- 1 | Id,SepalLengthCm,SepalWidthCm,PetalLengthCm,PetalWidthCm,Species 2 | 1,5.1,3.5,1.4,0.2,Iris-setosa 3 | 2,4.9,3.0,1.4,0.2,Iris-setosa 4 | 3,4.7,3.2,1.3,0.2,Iris-setosa 5 | 4,4.6,3.1,1.5,0.2,Iris-setosa 6 | 5,5.0,3.6,1.4,0.2,Iris-setosa 7 | 6,5.4,3.9,1.7,0.4,Iris-setosa 8 | 7,4.6,3.4,1.4,0.3,Iris-setosa 9 | 8,5.0,3.4,1.5,0.2,Iris-setosa 10 | 9,4.4,2.9,1.4,0.2,Iris-setosa 11 | 10,4.9,3.1,1.5,0.1,Iris-setosa 12 | 11,5.4,3.7,1.5,0.2,Iris-setosa 13 | 12,4.8,3.4,1.6,0.2,Iris-setosa 14 | 13,4.8,3.0,1.4,0.1,Iris-setosa 15 | 14,4.3,3.0,1.1,0.1,Iris-setosa 16 | 15,5.8,4.0,1.2,0.2,Iris-setosa 17 | 16,5.7,4.4,1.5,0.4,Iris-setosa 18 | 17,5.4,3.9,1.3,0.4,Iris-setosa 19 | 18,5.1,3.5,1.4,0.3,Iris-setosa 20 | 19,5.7,3.8,1.7,0.3,Iris-setosa 21 | 20,5.1,3.8,1.5,0.3,Iris-setosa 22 | 21,5.4,3.4,1.7,0.2,Iris-setosa 23 | 22,5.1,3.7,1.5,0.4,Iris-setosa 24 | 23,4.6,3.6,1.0,0.2,Iris-setosa 25 | 24,5.1,3.3,1.7,0.5,Iris-setosa 26 | 25,4.8,3.4,1.9,0.2,Iris-setosa 27 | 26,5.0,3.0,1.6,0.2,Iris-setosa 28 | 27,5.0,3.4,1.6,0.4,Iris-setosa 29 | 28,5.2,3.5,1.5,0.2,Iris-setosa 30 | 29,5.2,3.4,1.4,0.2,Iris-setosa 31 | 30,4.7,3.2,1.6,0.2,Iris-setosa 32 | 31,4.8,3.1,1.6,0.2,Iris-setosa 33 | 32,5.4,3.4,1.5,0.4,Iris-setosa 34 | 33,5.2,4.1,1.5,0.1,Iris-setosa 35 | 34,5.5,4.2,1.4,0.2,Iris-setosa 36 | 35,4.9,3.1,1.5,0.1,Iris-setosa 37 | 36,5.0,3.2,1.2,0.2,Iris-setosa 38 | 37,5.5,3.5,1.3,0.2,Iris-setosa 39 | 38,4.9,3.1,1.5,0.1,Iris-setosa 40 | 39,4.4,3.0,1.3,0.2,Iris-setosa 41 | 40,5.1,3.4,1.5,0.2,Iris-setosa 42 | 41,5.0,3.5,1.3,0.3,Iris-setosa 43 | 42,4.5,2.3,1.3,0.3,Iris-setosa 44 | 43,4.4,3.2,1.3,0.2,Iris-setosa 45 | 44,5.0,3.5,1.6,0.6,Iris-setosa 46 | 45,5.1,3.8,1.9,0.4,Iris-setosa 47 | 46,4.8,3.0,1.4,0.3,Iris-setosa 48 | 47,5.1,3.8,1.6,0.2,Iris-setosa 49 | 48,4.6,3.2,1.4,0.2,Iris-setosa 50 | 49,5.3,3.7,1.5,0.2,Iris-setosa 51 | 50,5.0,3.3,1.4,0.2,Iris-setosa 52 | 51,7.0,3.2,4.7,1.4,Iris-versicolor 53 | 52,6.4,3.2,4.5,1.5,Iris-versicolor 54 | 53,6.9,3.1,4.9,1.5,Iris-versicolor 55 | 54,5.5,2.3,4.0,1.3,Iris-versicolor 56 | 55,6.5,2.8,4.6,1.5,Iris-versicolor 57 | 56,5.7,2.8,4.5,1.3,Iris-versicolor 58 | 57,6.3,3.3,4.7,1.6,Iris-versicolor 59 | 58,4.9,2.4,3.3,1.0,Iris-versicolor 60 | 59,6.6,2.9,4.6,1.3,Iris-versicolor 61 | 60,5.2,2.7,3.9,1.4,Iris-versicolor 62 | 61,5.0,2.0,3.5,1.0,Iris-versicolor 63 | 62,5.9,3.0,4.2,1.5,Iris-versicolor 64 | 63,6.0,2.2,4.0,1.0,Iris-versicolor 65 | 64,6.1,2.9,4.7,1.4,Iris-versicolor 66 | 65,5.6,2.9,3.6,1.3,Iris-versicolor 67 | 66,6.7,3.1,4.4,1.4,Iris-versicolor 68 | 67,5.6,3.0,4.5,1.5,Iris-versicolor 69 | 68,5.8,2.7,4.1,1.0,Iris-versicolor 70 | 69,6.2,2.2,4.5,1.5,Iris-versicolor 71 | 70,5.6,2.5,3.9,1.1,Iris-versicolor 72 | 71,5.9,3.2,4.8,1.8,Iris-versicolor 73 | 72,6.1,2.8,4.0,1.3,Iris-versicolor 74 | 73,6.3,2.5,4.9,1.5,Iris-versicolor 75 | 74,6.1,2.8,4.7,1.2,Iris-versicolor 76 | 75,6.4,2.9,4.3,1.3,Iris-versicolor 77 | 76,6.6,3.0,4.4,1.4,Iris-versicolor 78 | 77,6.8,2.8,4.8,1.4,Iris-versicolor 79 | 78,6.7,3.0,5.0,1.7,Iris-versicolor 80 | 79,6.0,2.9,4.5,1.5,Iris-versicolor 81 | 80,5.7,2.6,3.5,1.0,Iris-versicolor 82 | 81,5.5,2.4,3.8,1.1,Iris-versicolor 83 | 82,5.5,2.4,3.7,1.0,Iris-versicolor 84 | 83,5.8,2.7,3.9,1.2,Iris-versicolor 85 | 84,6.0,2.7,5.1,1.6,Iris-versicolor 86 | 85,5.4,3.0,4.5,1.5,Iris-versicolor 87 | 86,6.0,3.4,4.5,1.6,Iris-versicolor 88 | 87,6.7,3.1,4.7,1.5,Iris-versicolor 89 | 88,6.3,2.3,4.4,1.3,Iris-versicolor 90 | 89,5.6,3.0,4.1,1.3,Iris-versicolor 91 | 90,5.5,2.5,4.0,1.3,Iris-versicolor 92 | 91,5.5,2.6,4.4,1.2,Iris-versicolor 93 | 92,6.1,3.0,4.6,1.4,Iris-versicolor 94 | 93,5.8,2.6,4.0,1.2,Iris-versicolor 95 | 94,5.0,2.3,3.3,1.0,Iris-versicolor 96 | 95,5.6,2.7,4.2,1.3,Iris-versicolor 97 | 96,5.7,3.0,4.2,1.2,Iris-versicolor 98 | 97,5.7,2.9,4.2,1.3,Iris-versicolor 99 | 98,6.2,2.9,4.3,1.3,Iris-versicolor 100 | 99,5.1,2.5,3.0,1.1,Iris-versicolor 101 | 100,5.7,2.8,4.1,1.3,Iris-versicolor 102 | 101,6.3,3.3,6.0,2.5,Iris-virginica 103 | 102,5.8,2.7,5.1,1.9,Iris-virginica 104 | 103,7.1,3.0,5.9,2.1,Iris-virginica 105 | 104,6.3,2.9,5.6,1.8,Iris-virginica 106 | 105,6.5,3.0,5.8,2.2,Iris-virginica 107 | 106,7.6,3.0,6.6,2.1,Iris-virginica 108 | 107,4.9,2.5,4.5,1.7,Iris-virginica 109 | 108,7.3,2.9,6.3,1.8,Iris-virginica 110 | 109,6.7,2.5,5.8,1.8,Iris-virginica 111 | 110,7.2,3.6,6.1,2.5,Iris-virginica 112 | 111,6.5,3.2,5.1,2.0,Iris-virginica 113 | 112,6.4,2.7,5.3,1.9,Iris-virginica 114 | 113,6.8,3.0,5.5,2.1,Iris-virginica 115 | 114,5.7,2.5,5.0,2.0,Iris-virginica 116 | 115,5.8,2.8,5.1,2.4,Iris-virginica 117 | 116,6.4,3.2,5.3,2.3,Iris-virginica 118 | 117,6.5,3.0,5.5,1.8,Iris-virginica 119 | 118,7.7,3.8,6.7,2.2,Iris-virginica 120 | 119,7.7,2.6,6.9,2.3,Iris-virginica 121 | 120,6.0,2.2,5.0,1.5,Iris-virginica 122 | 121,6.9,3.2,5.7,2.3,Iris-virginica 123 | 122,5.6,2.8,4.9,2.0,Iris-virginica 124 | 123,7.7,2.8,6.7,2.0,Iris-virginica 125 | 124,6.3,2.7,4.9,1.8,Iris-virginica 126 | 125,6.7,3.3,5.7,2.1,Iris-virginica 127 | 126,7.2,3.2,6.0,1.8,Iris-virginica 128 | 127,6.2,2.8,4.8,1.8,Iris-virginica 129 | 128,6.1,3.0,4.9,1.8,Iris-virginica 130 | 129,6.4,2.8,5.6,2.1,Iris-virginica 131 | 130,7.2,3.0,5.8,1.6,Iris-virginica 132 | 131,7.4,2.8,6.1,1.9,Iris-virginica 133 | 132,7.9,3.8,6.4,2.0,Iris-virginica 134 | 133,6.4,2.8,5.6,2.2,Iris-virginica 135 | 134,6.3,2.8,5.1,1.5,Iris-virginica 136 | 135,6.1,2.6,5.6,1.4,Iris-virginica 137 | 136,7.7,3.0,6.1,2.3,Iris-virginica 138 | 137,6.3,3.4,5.6,2.4,Iris-virginica 139 | 138,6.4,3.1,5.5,1.8,Iris-virginica 140 | 139,6.0,3.0,4.8,1.8,Iris-virginica 141 | 140,6.9,3.1,5.4,2.1,Iris-virginica 142 | 141,6.7,3.1,5.6,2.4,Iris-virginica 143 | 142,6.9,3.1,5.1,2.3,Iris-virginica 144 | 143,5.8,2.7,5.1,1.9,Iris-virginica 145 | 144,6.8,3.2,5.9,2.3,Iris-virginica 146 | 145,6.7,3.3,5.7,2.5,Iris-virginica 147 | 146,6.7,3.0,5.2,2.3,Iris-virginica 148 | 147,6.3,2.5,5.0,1.9,Iris-virginica 149 | 148,6.5,3.0,5.2,2.0,Iris-virginica 150 | 149,6.2,3.4,5.4,2.3,Iris-virginica 151 | 150,5.9,3.0,5.1,1.8,Iris-virginica 152 | -------------------------------------------------------------------------------- /Week 7 - Logistic Regression/figs/logistic_regression.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Computational-Intelligence-Fall18/Computational-Intelligence-Tutorials/18018dd5258ec0563513076c8e46bba8113d65b4/Week 7 - Logistic Regression/figs/logistic_regression.png -------------------------------------------------------------------------------- /Week 7 - Logistic Regression/utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import seaborn as sb 4 | import pandas as pd 5 | from sklearn import datasets 6 | 7 | def plot_prediction(X, Y, predict_func, params): 8 | X = np.array(X) 9 | Y = np.array(Y) 10 | plt.figure(figsize=(10, 6)) 11 | plt.scatter(X[Y == 0][:, 0], X[Y == 0][:, 1], color='b', label='0') 12 | plt.scatter(X[Y == 1][:, 0], X[Y == 1][:, 1], color='r', label='1') 13 | plt.legend() 14 | x1_min, x1_max = X[:,0].min(), X[:,0].max(), 15 | x2_min, x2_max = X[:,1].min(), X[:,1].max(), 16 | xx1, xx2 = np.meshgrid(np.linspace(x1_min, x1_max), np.linspace(x2_min, x2_max)) 17 | grid = np.c_[xx1.ravel(), xx2.ravel()] 18 | probs = predict_func(params['w'], params['b'], grid.T).reshape(xx1.shape) 19 | plt.contour(xx1, xx2, probs, [0.5], linewidths=1, colors='black') --------------------------------------------------------------------------------