\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 | ""
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 | ""
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 | ""
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 | ""
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 | ""
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 | ""
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 | "\n",
146 | "### Strategy\n",
147 | "\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 | "\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')
--------------------------------------------------------------------------------