├── Ejercicios ├── Optimizacion de rotor │ └── optrot │ │ ├── __init__.py │ │ └── rotor.py ├── Hormiguero │ ├── ants │ │ ├── __init__.py │ │ └── _ants.py │ └── Hormiguero.ipynb ├── Laberinto │ └── laberinto │ │ ├── __init__.py │ │ ├── algen.py │ │ └── laberinto.py ├── El vecindario racista │ ├── vecindario │ │ ├── __init__.py │ │ └── _vecindario.py │ └── El vecindario racista.ipynb ├── Laberinto (resumido) │ └── laberinto │ │ ├── __init__.py │ │ ├── algen.py │ │ └── laberinto.py ├── Equilibrios de Nash │ └── nash.py └── El problema de los matrimonios estables │ └── matrimonios_estables.ipynb ├── codigo ├── __init__.py ├── _funciones_metaheuristica.py └── algen.py ├── imagenes ├── Angel.jpg ├── Siro.jpg ├── drag.png ├── easom.png ├── sean.jpg ├── Carlos.jpg ├── Knowmore.jpg ├── hormigas.png ├── Dessalles.jpg ├── SC-Warren.gif ├── eggholder.png ├── SC-aplicacion.gif ├── Star_of_Chaos.jpg ├── bohachevski.png ├── john_holland.jpg ├── matriz-grafos.gif ├── sayama_hiroki.jpg ├── turingpattern.jpg ├── 0_1_2_2_1_espia.png ├── 1_0_2_1_2_espia.png ├── 1_1_2_1_1_espia.png ├── aeropython_logo.png ├── goldstein_price.png ├── imm_cross_tour.gif ├── 1_0_2_1_2_combate.png ├── 1_1_2_1_1_combate.png ├── Caracol y automata.jpg ├── SC-tipos-modelos.gif ├── genome_animation.gif ├── points_animation.gif ├── 0_1_2_2_1_combate_a.png ├── 0_1_2_2_1_combate_b.png ├── 0_1_2_2_1_evolucion.png ├── 1_0_2_1_2_evolucion.png ├── 1_1_2_1_1_evolucion.png ├── Game_of_life_animated_glider.gif └── Complex_systems_organizational_map.jpg ├── static ├── aeropython_name_mini.png └── style.css ├── LICENSE ├── README.md ├── .gitignore ├── Agradecimientos.ipynb ├── Presentacion ponentes.ipynb ├── Bloque B1 - Metaheurística-Vacío.ipynb └── Bloque C - Ajuste y paquetes.ipynb /Ejercicios/Optimizacion de rotor/optrot/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Ejercicios/Hormiguero/ants/__init__.py: -------------------------------------------------------------------------------- 1 | from._ants import * 2 | -------------------------------------------------------------------------------- /codigo/__init__.py: -------------------------------------------------------------------------------- 1 | from._funciones_metaheuristica import * 2 | -------------------------------------------------------------------------------- /Ejercicios/Laberinto/laberinto/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | #from._laberinto import * 3 | -------------------------------------------------------------------------------- /Ejercicios/El vecindario racista/vecindario/__init__.py: -------------------------------------------------------------------------------- 1 | from._vecindario import * 2 | -------------------------------------------------------------------------------- /Ejercicios/Laberinto (resumido)/laberinto/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | #from._laberinto import * 3 | -------------------------------------------------------------------------------- /imagenes/Angel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/Angel.jpg -------------------------------------------------------------------------------- /imagenes/Siro.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/Siro.jpg -------------------------------------------------------------------------------- /imagenes/drag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/drag.png -------------------------------------------------------------------------------- /imagenes/easom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/easom.png -------------------------------------------------------------------------------- /imagenes/sean.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/sean.jpg -------------------------------------------------------------------------------- /imagenes/Carlos.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/Carlos.jpg -------------------------------------------------------------------------------- /imagenes/Knowmore.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/Knowmore.jpg -------------------------------------------------------------------------------- /imagenes/hormigas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/hormigas.png -------------------------------------------------------------------------------- /imagenes/Dessalles.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/Dessalles.jpg -------------------------------------------------------------------------------- /imagenes/SC-Warren.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/SC-Warren.gif -------------------------------------------------------------------------------- /imagenes/eggholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/eggholder.png -------------------------------------------------------------------------------- /imagenes/SC-aplicacion.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/SC-aplicacion.gif -------------------------------------------------------------------------------- /imagenes/Star_of_Chaos.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/Star_of_Chaos.jpg -------------------------------------------------------------------------------- /imagenes/bohachevski.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/bohachevski.png -------------------------------------------------------------------------------- /imagenes/john_holland.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/john_holland.jpg -------------------------------------------------------------------------------- /imagenes/matriz-grafos.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/matriz-grafos.gif -------------------------------------------------------------------------------- /imagenes/sayama_hiroki.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/sayama_hiroki.jpg -------------------------------------------------------------------------------- /imagenes/turingpattern.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/turingpattern.jpg -------------------------------------------------------------------------------- /imagenes/0_1_2_2_1_espia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/0_1_2_2_1_espia.png -------------------------------------------------------------------------------- /imagenes/1_0_2_1_2_espia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/1_0_2_1_2_espia.png -------------------------------------------------------------------------------- /imagenes/1_1_2_1_1_espia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/1_1_2_1_1_espia.png -------------------------------------------------------------------------------- /imagenes/aeropython_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/aeropython_logo.png -------------------------------------------------------------------------------- /imagenes/goldstein_price.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/goldstein_price.png -------------------------------------------------------------------------------- /imagenes/imm_cross_tour.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/imm_cross_tour.gif -------------------------------------------------------------------------------- /imagenes/1_0_2_1_2_combate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/1_0_2_1_2_combate.png -------------------------------------------------------------------------------- /imagenes/1_1_2_1_1_combate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/1_1_2_1_1_combate.png -------------------------------------------------------------------------------- /imagenes/Caracol y automata.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/Caracol y automata.jpg -------------------------------------------------------------------------------- /imagenes/SC-tipos-modelos.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/SC-tipos-modelos.gif -------------------------------------------------------------------------------- /imagenes/genome_animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/genome_animation.gif -------------------------------------------------------------------------------- /imagenes/points_animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/points_animation.gif -------------------------------------------------------------------------------- /static/aeropython_name_mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/static/aeropython_name_mini.png -------------------------------------------------------------------------------- /imagenes/0_1_2_2_1_combate_a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/0_1_2_2_1_combate_a.png -------------------------------------------------------------------------------- /imagenes/0_1_2_2_1_combate_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/0_1_2_2_1_combate_b.png -------------------------------------------------------------------------------- /imagenes/0_1_2_2_1_evolucion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/0_1_2_2_1_evolucion.png -------------------------------------------------------------------------------- /imagenes/1_0_2_1_2_evolucion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/1_0_2_1_2_evolucion.png -------------------------------------------------------------------------------- /imagenes/1_1_2_1_1_evolucion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/1_1_2_1_1_evolucion.png -------------------------------------------------------------------------------- /imagenes/Game_of_life_animated_glider.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/Game_of_life_animated_glider.gif -------------------------------------------------------------------------------- /imagenes/Complex_systems_organizational_map.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/Taller-Algoritmos-Geneticos-PyConEs16/master/imagenes/Complex_systems_organizational_map.jpg -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 AeroPython 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Taller-PyConEs-2016 2 | 3 | 4 | ##Simplifica tu vida con sistemas complejos y algoritmos genéticos 5 | 6 | 7 | 8 | ## VIERNES 7/OCT/2016 9 | 10 | ### CARLOS DORADO, SIRO MORENO 11 | #### NIVEL: INICIACIÓN - INTERMEDIO 12 | ## Información General 13 | 14 | Hola pythonistas! Bienvenidos a nuestro taller **Simplifica tu vida con sistemas complejos y algoritmos genéticos** 15 | 16 | Para poder seguir el taller de forma autónoma será conveniente traer portátil con Anaconda instalado. Si te surgen dudas puedes seguir esta guía de instalación: 17 | 18 | [http://nbviewer.jupyter.org/github/AeroPython/Curso_AeroPython/blob/master/notebooks_completos/Clase0_Bienvenido.ipynb](http://nbviewer.jupyter.org/github/AeroPython/Curso_AeroPython/blob/master/notebooks_completos/Clase0_Bienvenido.ipynb) 19 | 20 | 21 | Usaremos [Anaconda Python 3.5.1 distribution](https://www.continuum.io/downloads) y los siguientes paquetes: 22 | 23 | * IPython 24 | * IPython-Notebook 25 | * matplotlib 26 | * Numpy 27 | 28 | ¡Trata de instalarlos antes! 29 | 30 | [Síguenos en Twitter](https://twitter.com/AeroPython) 31 | 32 | AeroPython -------------------------------------------------------------------------------- /codigo/_funciones_metaheuristica.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import numpy as np 5 | 6 | def caja_negra (x): 7 | 8 | d = np.size(x) 9 | result = 0 10 | 11 | for ii in range (d): 12 | result += x[ii]**3 - 16*x[ii]**2 + 5*x[ii] 13 | 14 | return 0.5*np.sqrt(np.abs(result)) - x[ii] + 4.856777 15 | 16 | def bohachevski (x): 17 | 18 | term1 = x[0]**2 19 | term2 = 2*x[1]**2 20 | term3 = -0.3 * np.cos(3*np.pi*x[0]) 21 | term4 = -0.4 * np.cos(4*np.pi*x[1]) 22 | 23 | return term1 + term2 + term3 + term4 + 0.7 24 | 25 | 26 | def goldstein_price(x): 27 | 28 | fact1a = (x[0] + x[1] + 1)**2 29 | fact1b = 19 - 14*x[0] + 3*x[0]**2 - 14*x[1] + 6*x[0]*x[1] + 3*x[1]**2 30 | fact1 = 1 + fact1a*fact1b 31 | 32 | fact2a = (2*x[0] - 3*x[1])**2 33 | fact2b = 18 - 32*x[0] + 12*x[0]**2 + 48*x[1] - 36*x[0]*x[1] + 27*x[1]**2 34 | fact2 = 30 + fact2a*fact2b 35 | 36 | return fact1*fact2 37 | 38 | def easom(x): 39 | 40 | fact1 = -np.cos(x[0])*np.cos(x[1]) 41 | fact2 = np.exp(-(x[0]-np.pi)**2-(x[1]-np.pi)**2) 42 | 43 | return fact1*fact2; 44 | 45 | def eggholder(x): 46 | 47 | term1 = -(x[1]+47) * np.sin(np.sqrt(abs(x[1]+x[0]/2+47))) 48 | term2 = -x[0] * np.sin(np.sqrt(abs(x[0]-(x[1]+47)))) 49 | 50 | return term1 + term2 -------------------------------------------------------------------------------- /.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 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | -------------------------------------------------------------------------------- /Ejercicios/Equilibrios de Nash/nash.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from numpy.lib.stride_tricks import as_strided 3 | import random as random 4 | import matplotlib.pyplot as plt 5 | from matplotlib.patches import Circle 6 | 7 | def create_world (lines = 10, columns = 0, proportion = 0.5): 8 | """ 9 | This function creates a random world consisting of a matrix of individuals. 10 | There are two types of individuals, in a certain proportion 11 | """ 12 | 13 | if columns == 0: 14 | columns = lines 15 | 16 | len_world = lines * columns 17 | world = round(proportion * len_world) * [1] 18 | world += (len_world - len(world)) * [0] 19 | random.shuffle(world) 20 | 21 | return np.array(world).reshape(lines, columns) 22 | 23 | 24 | def plot_world (world): 25 | ''' 26 | This function takes a numpy 2D array with string elements 27 | and scatters it with the colours corresponding to these strings 28 | ''' 29 | 30 | #This sets the colours for the plot 31 | cmap = plt.cm.jet 32 | world = world*160 + 90 33 | 34 | 35 | fig = plt.figure(figsize = (10,10)) 36 | ax = fig.add_subplot(111, aspect='equal') 37 | 38 | for ii in range (world.shape[0]): 39 | 40 | x = range(world.shape[1]) 41 | y = world.shape[1] * [ii] 42 | colours = world [ii] 43 | 44 | for (x, y, c) in zip(x, y, colours): 45 | ax.add_artist(Circle(xy=(x, y), radius=0.45, color=cmap(c))) 46 | 47 | plt.axis('off') 48 | ax.set_xlim(-1, world.shape[1]) 49 | ax.set_ylim(-1, world.shape[0]) 50 | 51 | 52 | 53 | def sliding_window(arr, window_size): 54 | """ 55 | #by pv on stack overflow 56 | Construct a sliding window view of the array 57 | """ 58 | arr = np.asarray(arr) 59 | window_size = int(window_size) 60 | if arr.ndim != 2: 61 | raise ValueError("need 2-D input") 62 | if not (window_size > 0): 63 | raise ValueError("need a positive window size") 64 | shape = (arr.shape[0] - window_size + 1, 65 | arr.shape[1] - window_size + 1, 66 | window_size, window_size) 67 | if shape[0] <= 0: 68 | shape = (1, shape[1], arr.shape[0], shape[3]) 69 | if shape[1] <= 0: 70 | shape = (shape[0], 1, shape[2], arr.shape[1]) 71 | strides = (arr.shape[1]*arr.itemsize, arr.itemsize, 72 | arr.shape[1]*arr.itemsize, arr.itemsize) 73 | return as_strided(arr, shape=shape, strides=strides) 74 | 75 | def neighbourhood(arr, i, j, d): 76 | """ 77 | #by pv on stack overflow 78 | Return d-th neighbors of cell (i, j) 79 | """ 80 | 81 | w = sliding_window(arr, 2*d+1) 82 | 83 | ix = np.clip(i - d, 0, w.shape[0]-1) 84 | jx = np.clip(j - d, 0, w.shape[1]-1) 85 | 86 | i0 = max(0, i - d - ix) 87 | j0 = max(0, j - d - jx) 88 | i1 = w.shape[2] - max(0, d - i + ix) 89 | j1 = w.shape[3] - max(0, d - j + jx) 90 | 91 | return w[ix, jx][i0:i1,j0:j1].ravel() 92 | 93 | def weighted_choice_sub(weights): 94 | """ 95 | weights = [0.9, 0.05, 0.05] 96 | N = 100000 97 | lista = [weighted_choice_sub(weights) for ii in range(N)] 98 | print( lista.count(0)/N, lista.count(1)/N, lista.count(2)/N) 99 | """ 100 | rnd = random.random() * sum(weights) 101 | for i, w in enumerate(weights): 102 | rnd -= w 103 | if rnd < 0: 104 | return i -------------------------------------------------------------------------------- /static/style.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 127 | 143 | -------------------------------------------------------------------------------- /codigo/algen.py: -------------------------------------------------------------------------------- 1 | import random as random 2 | 3 | 4 | 5 | #This function was taken from Eli Bendersky's website 6 | #It returns an index of a list called "weights", 7 | #where the content of each element in "weights" is the probability of this index to be returned. 8 | #For this function to be as fast as possible we need to pass it a list of weights in descending order. 9 | def weighted_choice_sub(weights): 10 | rnd = random.random() * sum(weights) 11 | for i, w in enumerate(weights): 12 | rnd -= w 13 | if rnd < 0: 14 | return i 15 | 16 | 17 | 18 | 19 | generate_random_binary_list = lambda n: [random.randint(0,1) for b in range(1,n+1)] 20 | 21 | 22 | 23 | 24 | class Individual (object): 25 | 26 | def __init__(self, genome): 27 | 28 | self.genome = genome 29 | self.traits = {} 30 | self.performances = {} 31 | self.fitness = 0 32 | 33 | 34 | 35 | def generate_genome (dict_genes): 36 | 37 | #We first calculate the total number of bits that the genome must contain 38 | number_of_bits = sum([dict_genes[trait] for trait in dict_genes]) 39 | 40 | #And we return a random genome of this length 41 | return generate_random_binary_list(number_of_bits) 42 | 43 | 44 | 45 | def calculate_traits (individual, dict_genes): 46 | #This function must decipher the genome and return the traits of the individual. 47 | #Normally, the genome contains binary numerical values for the different traits. 48 | 49 | dict_traits = {} 50 | index = 0 51 | 52 | for trait in dict_genes: 53 | dict_traits[trait] = int(''.join(str(bit) for bit in individual.genome[index : index+dict_genes[trait]]), 2) 54 | index += dict_genes[trait] 55 | 56 | individual.traits = dict_traits 57 | 58 | 59 | 60 | 61 | def immigration (society, target_population, 62 | calculate_performances, calculate_fitness, 63 | dict_genes, Object = Individual, *args): 64 | 65 | while len(society) < target_population: 66 | 67 | new_individual = Object (generate_genome (dict_genes), args) 68 | calculate_traits (new_individual, dict_genes) 69 | calculate_performances (new_individual) 70 | calculate_fitness (new_individual) 71 | 72 | society.append (new_individual) 73 | 74 | 75 | 76 | 77 | 78 | def crossover (society, reproduction_rate, mutation_rate, 79 | calculate_performances, calculate_fitness, 80 | dict_genes, Object = Individual, *args): 81 | 82 | #First we create a list with the fitness values of every individual in the society 83 | fitness_list = [individual.fitness for individual in society] 84 | 85 | #We sort the individuals in the society in descending order of fitness. 86 | society_sorted = [x for (y, x) in sorted(zip(fitness_list, society), key=lambda x: x[0], reverse=True)] 87 | 88 | #We then create a list of relative probabilities in descending order, 89 | #so that the fittest individual in the society has N times more chances to reproduce than the least fit, 90 | #where N is the number of individuals in the society. 91 | probability = [i for i in reversed(range(1,len(society_sorted)+1))] 92 | 93 | #We create a list of weights with the probabilities of non-mutation and mutation 94 | mutation = [1 - mutation_rate, mutation_rate] 95 | 96 | #For every new individual to be created through reproduction: 97 | for i in range (int(len(society) * reproduction_rate)): 98 | 99 | #We select two parents randomly, using the list of probabilities in "probability". 100 | father, mother = society_sorted[weighted_choice_sub(probability)], society_sorted[weighted_choice_sub(probability)] 101 | 102 | #We randomly select two cutting points for the genome. 103 | a, b = random.randrange(0, len(father.genome)), random.randrange(0, len(father.genome)) 104 | 105 | #And we create the genome of the child putting together the genome slices of the parents in the cutting points. 106 | child_genome = father.genome[0:min(a,b)]+mother.genome[min(a,b):max(a,b)]+father.genome[max(a,b):] 107 | 108 | #For every bit in the not-yet-born child, we generate a list containing 109 | #1's in the positions where the genome must mutate (i.e. the bit must switch its value) 110 | #and 0's in the positions where the genome must stay the same. 111 | n = [weighted_choice_sub(mutation) for ii in range(len(child_genome))] 112 | 113 | #This line switches the bits of the genome of the child that must mutate. 114 | mutant_child_genome = [abs(n[i] - child_genome[i]) for i in range(len(child_genome))] 115 | 116 | #We finally append the newborn individual to the society 117 | newborn = Object(mutant_child_genome, args) 118 | calculate_traits (newborn, dict_genes) 119 | calculate_performances (newborn) 120 | calculate_fitness (newborn) 121 | society.append(newborn) 122 | 123 | 124 | 125 | def tournament(society, target_population): 126 | 127 | while len(society) > target_population: 128 | 129 | #index1, index2 = random.randrange(0, len(society)), random.randrange(0, len(society)) 130 | 131 | #if society[index1].fitness > society[index2].fitness: 132 | # society.pop(index2) 133 | #else: 134 | # society.pop(index1) 135 | 136 | fitness_list = [individual.fitness for individual in society] 137 | society.pop(fitness_list.index(min(fitness_list))) -------------------------------------------------------------------------------- /Ejercicios/Laberinto/laberinto/algen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """Ejercicio del Laberinto 5 | 6 | Taller de la PyConEs 2015: Simplifica tu vida con sistemas complejos y algoritmos genéticos 7 | 8 | Este script contiene las funciones y clases necesarias para el algoritmo genético del 9 | ejercicio del laberinto. 10 | 11 | 12 | Este script usa arrays de numpy, aunque no debería ser difícil para alguien con experiencia 13 | sustituírlos por otras estructuras si es necesario. 14 | 15 | También usa la librería Matplotlib en las funciones que dibujan resultados.""" 16 | 17 | 18 | 19 | import random as random 20 | 21 | 22 | 23 | #This function was taken from Eli Bendersky's website 24 | #It returns an index of a list called "weights", 25 | #where the content of each element in "weights" is the probability of this index to be returned. 26 | #For this function to be as fast as possible we need to pass it a list of weights in descending order. 27 | def weighted_choice_sub(weights): 28 | rnd = random.random() * sum(weights) 29 | for i, w in enumerate(weights): 30 | rnd -= w 31 | if rnd < 0: 32 | return i 33 | 34 | 35 | 36 | 37 | generate_random_binary_list = lambda n: [random.randint(0,1) for b in range(1,n+1)] 38 | 39 | 40 | 41 | 42 | class Individual (object): 43 | 44 | def __init__(self, genome): 45 | 46 | self.genome = genome 47 | self.traits = {} 48 | self.performances = {} 49 | self.fitness = 0 50 | 51 | 52 | 53 | def generate_genome (dict_genes): 54 | 55 | #We first calculate the total number of bits that the genome must contain 56 | number_of_bits = sum([dict_genes[trait] for trait in dict_genes]) 57 | 58 | #And we return a random genome of this length 59 | return generate_random_binary_list(number_of_bits) 60 | 61 | 62 | 63 | def calculate_traits (individual, dict_genes): 64 | #This function must decipher the genome and return the traits of the individual. 65 | #Normally, the genome contains binary numerical values for the different traits. 66 | 67 | dict_traits = {} 68 | index = 0 69 | 70 | for trait in dict_genes: 71 | dict_traits[trait] = int(''.join(str(bit) for bit in individual.genome[index : index+dict_genes[trait]]), 2) 72 | index += dict_genes[trait] 73 | 74 | individual.traits = dict_traits 75 | 76 | 77 | 78 | 79 | def immigration (society, target_population, 80 | calculate_performances, calculate_fitness, 81 | dict_genes, Object = Individual, *args): 82 | 83 | while len(society) < target_population: 84 | 85 | new_individual = Object (generate_genome (dict_genes), args) 86 | calculate_traits (new_individual, dict_genes) 87 | calculate_performances (new_individual) 88 | calculate_fitness (new_individual) 89 | 90 | society.append (new_individual) 91 | 92 | 93 | 94 | 95 | 96 | def crossover (society, reproduction_rate, mutation_rate, 97 | calculate_performances, calculate_fitness, 98 | dict_genes, Object = Individual, *args): 99 | 100 | #First we create a list with the fitness values of every individual in the society 101 | fitness_list = [individual.fitness for individual in society] 102 | 103 | #We sort the individuals in the society in descending order of fitness. 104 | society_sorted = [x for (y, x) in sorted(zip(fitness_list, society), key=lambda x: x[0], reverse=True)] 105 | 106 | #We then create a list of relative probabilities in descending order, 107 | #so that the fittest individual in the society has N times more chances to reproduce than the least fit, 108 | #where N is the number of individuals in the society. 109 | probability = [i for i in reversed(range(1,len(society_sorted)+1))] 110 | 111 | #We create a list of weights with the probabilities of non-mutation and mutation 112 | mutation = [1 - mutation_rate, mutation_rate] 113 | 114 | #For every new individual to be created through reproduction: 115 | for i in range (int(len(society) * reproduction_rate)): 116 | 117 | #We select two parents randomly, using the list of probabilities in "probability". 118 | father, mother = society_sorted[weighted_choice_sub(probability)], society_sorted[weighted_choice_sub(probability)] 119 | 120 | #We randomly select two cutting points for the genome. 121 | a, b = random.randrange(0, len(father.genome)), random.randrange(0, len(father.genome)) 122 | 123 | #And we create the genome of the child putting together the genome slices of the parents in the cutting points. 124 | child_genome = father.genome[0:min(a,b)]+mother.genome[min(a,b):max(a,b)]+father.genome[max(a,b):] 125 | 126 | #For every bit in the not-yet-born child, we generate a list containing 127 | #1's in the positions where the genome must mutate (i.e. the bit must switch its value) 128 | #and 0's in the positions where the genome must stay the same. 129 | n = [weighted_choice_sub(mutation) for ii in range(len(child_genome))] 130 | 131 | #This line switches the bits of the genome of the child that must mutate. 132 | mutant_child_genome = [abs(n[i] - child_genome[i]) for i in range(len(child_genome))] 133 | 134 | #We finally append the newborn individual to the society 135 | newborn = Object(mutant_child_genome, args) 136 | calculate_traits (newborn, dict_genes) 137 | calculate_performances (newborn) 138 | calculate_fitness (newborn) 139 | society.append(newborn) 140 | 141 | 142 | 143 | def tournament(society, target_population): 144 | 145 | while len(society) > target_population: 146 | 147 | #index1, index2 = random.randrange(0, len(society)), random.randrange(0, len(society)) 148 | 149 | #if society[index1].fitness > society[index2].fitness: 150 | # society.pop(index2) 151 | #else: 152 | # society.pop(index1) 153 | 154 | fitness_list = [individual.fitness for individual in society] 155 | society.pop(fitness_list.index(min(fitness_list))) -------------------------------------------------------------------------------- /Ejercicios/Laberinto (resumido)/laberinto/algen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """Ejercicio del Laberinto 5 | 6 | Taller de la PyConEs 2015: Simplifica tu vida con sistemas complejos y algoritmos genéticos 7 | 8 | Este script contiene las funciones y clases necesarias para el algoritmo genético del 9 | ejercicio del laberinto. 10 | 11 | 12 | Este script usa arrays de numpy, aunque no debería ser difícil para alguien con experiencia 13 | sustituírlos por otras estructuras si es necesario. 14 | 15 | También usa la librería Matplotlib en las funciones que dibujan resultados.""" 16 | 17 | 18 | 19 | import random as random 20 | 21 | 22 | 23 | #This function was taken from Eli Bendersky's website 24 | #It returns an index of a list called "weights", 25 | #where the content of each element in "weights" is the probability of this index to be returned. 26 | #For this function to be as fast as possible we need to pass it a list of weights in descending order. 27 | def weighted_choice_sub(weights): 28 | rnd = random.random() * sum(weights) 29 | for i, w in enumerate(weights): 30 | rnd -= w 31 | if rnd < 0: 32 | return i 33 | 34 | 35 | 36 | 37 | generate_random_binary_list = lambda n: [random.randint(0,1) for b in range(1,n+1)] 38 | 39 | 40 | 41 | 42 | class Individual (object): 43 | 44 | def __init__(self, genome): 45 | 46 | self.genome = genome 47 | self.traits = {} 48 | self.performances = {} 49 | self.fitness = 0 50 | 51 | 52 | 53 | def generate_genome (dict_genes): 54 | 55 | #We first calculate the total number of bits that the genome must contain 56 | number_of_bits = sum([dict_genes[trait] for trait in dict_genes]) 57 | 58 | #And we return a random genome of this length 59 | return generate_random_binary_list(number_of_bits) 60 | 61 | 62 | 63 | def calculate_traits (individual, dict_genes): 64 | #This function must decipher the genome and return the traits of the individual. 65 | #Normally, the genome contains binary numerical values for the different traits. 66 | 67 | dict_traits = {} 68 | index = 0 69 | 70 | for trait in dict_genes: 71 | dict_traits[trait] = int(''.join(str(bit) for bit in individual.genome[index : index+dict_genes[trait]]), 2) 72 | index += dict_genes[trait] 73 | 74 | individual.traits = dict_traits 75 | 76 | 77 | 78 | 79 | def immigration (society, target_population, 80 | calculate_performances, calculate_fitness, 81 | dict_genes, Object = Individual, *args): 82 | 83 | while len(society) < target_population: 84 | 85 | new_individual = Object (generate_genome (dict_genes), args) 86 | calculate_traits (new_individual, dict_genes) 87 | calculate_performances (new_individual) 88 | calculate_fitness (new_individual) 89 | 90 | society.append (new_individual) 91 | 92 | 93 | 94 | 95 | 96 | def crossover (society, reproduction_rate, mutation_rate, 97 | calculate_performances, calculate_fitness, 98 | dict_genes, Object = Individual, *args): 99 | 100 | #First we create a list with the fitness values of every individual in the society 101 | fitness_list = [individual.fitness for individual in society] 102 | 103 | #We sort the individuals in the society in descending order of fitness. 104 | society_sorted = [x for (y, x) in sorted(zip(fitness_list, society), key=lambda x: x[0], reverse=True)] 105 | 106 | #We then create a list of relative probabilities in descending order, 107 | #so that the fittest individual in the society has N times more chances to reproduce than the least fit, 108 | #where N is the number of individuals in the society. 109 | probability = [i for i in reversed(range(1,len(society_sorted)+1))] 110 | 111 | #We create a list of weights with the probabilities of non-mutation and mutation 112 | mutation = [1 - mutation_rate, mutation_rate] 113 | 114 | #For every new individual to be created through reproduction: 115 | for i in range (int(len(society) * reproduction_rate)): 116 | 117 | #We select two parents randomly, using the list of probabilities in "probability". 118 | father, mother = society_sorted[weighted_choice_sub(probability)], society_sorted[weighted_choice_sub(probability)] 119 | 120 | #We randomly select two cutting points for the genome. 121 | a, b = random.randrange(0, len(father.genome)), random.randrange(0, len(father.genome)) 122 | 123 | #And we create the genome of the child putting together the genome slices of the parents in the cutting points. 124 | child_genome = father.genome[0:min(a,b)]+mother.genome[min(a,b):max(a,b)]+father.genome[max(a,b):] 125 | 126 | #For every bit in the not-yet-born child, we generate a list containing 127 | #1's in the positions where the genome must mutate (i.e. the bit must switch its value) 128 | #and 0's in the positions where the genome must stay the same. 129 | n = [weighted_choice_sub(mutation) for ii in range(len(child_genome))] 130 | 131 | #This line switches the bits of the genome of the child that must mutate. 132 | mutant_child_genome = [abs(n[i] - child_genome[i]) for i in range(len(child_genome))] 133 | 134 | #We finally append the newborn individual to the society 135 | newborn = Object(mutant_child_genome, args) 136 | calculate_traits (newborn, dict_genes) 137 | calculate_performances (newborn) 138 | calculate_fitness (newborn) 139 | society.append(newborn) 140 | 141 | 142 | 143 | def tournament(society, target_population): 144 | 145 | while len(society) > target_population: 146 | 147 | #index1, index2 = random.randrange(0, len(society)), random.randrange(0, len(society)) 148 | 149 | #if society[index1].fitness > society[index2].fitness: 150 | # society.pop(index2) 151 | #else: 152 | # society.pop(index1) 153 | 154 | fitness_list = [individual.fitness for individual in society] 155 | society.pop(fitness_list.index(min(fitness_list))) -------------------------------------------------------------------------------- /Ejercicios/Optimizacion de rotor/optrot/rotor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """Ejercicio del Algoritmo de optimización ingenieril 5 | 6 | Taller de la PyConEs 2015: Simplifica tu vida con sistemas complejos y algoritmos genéticos 7 | 8 | Este script contiene las funciones necesarias para calcular un rotor o hélice. 9 | 10 | 11 | Este script usa arrays de numpy, aunque no debería ser difícil para alguien con experiencia 12 | sustituírlos por otras estructuras si es necesario. 13 | 14 | """ 15 | 16 | 17 | import numpy as np 18 | 19 | def airf_aerodata(c, v, alpha, x, rho): 20 | '''Devuelve los coeficientes de sustentación y resistencia. 21 | c = cuerda 22 | v = velocidad 23 | alpha = ángulo de ataque''' 24 | 25 | #Calcular reynolds 26 | factor_Re = 0.5 + 8 *(1.5 / (1 + c * v * rho) ) 27 | factor_Re_2 = 1 / (1.2 - factor_Re) 28 | #calcular cl 29 | alpha_rad = alpha * np.pi/180 30 | s_fun = 0.5 - np.arctan((alpha - 20) * 0.4)/np.pi 31 | s_fun_2 = 0.5 - np.arctan((-alpha - 20) * 0.4)/np.pi 32 | cl_base = (alpha_rad + 0.) * 2 * np.pi 33 | cl = cl_base * s_fun * s_fun_2 * (0.7 + 0.3 * np.sqrt(x)) 34 | cd_base = 0.1 * cl_base**2 + 0.01 * factor_Re 35 | cd_base_2 = 0.55 #+ 0.01 * factor_Re 36 | cd = 0.4 * cd_base * s_fun * s_fun_2 + cd_base_2 * (1 - s_fun) + cd_base_2 * (1 - s_fun_2) 37 | # print(factor_Re, factor_Re_2) 38 | 39 | return cl, cd#, factor_Re 40 | 41 | 42 | def airf_slope (alpha, v, c, x, rho = 0.0208): 43 | ''' 44 | Calcula la pendiente de la curva de sustentación. 45 | Se alimenta con 4 arrays de dimensión N: 46 | - c, cuerda del perfil en metros 47 | - v, velocidad del en metros 48 | - alpha, ángulo de ataque del perfil en RADIANES 49 | - x, posición del perfil (0 = raíz, 0 = punta) 50 | 51 | Devuelve en un vector de dimensión N las pendientes de la curva de sustentación para cada perfil. 52 | 53 | ''' 54 | alpha_0 = alpha *180 / np.pi #La función que calcula cl usa grados 55 | 56 | cl = airf_aerodata(c, v, alpha_0, x, rho = rho)[0] 57 | 58 | #Esta parte es por si hay algún alpha = 0, para evitar el error de matemáticas. 59 | for ii in range(alpha.shape[0]): 60 | if abs(alpha[ii]) < 0.00000001: 61 | alpha[ii] = 0.00000001 62 | 63 | slope = cl / alpha 64 | 65 | return slope 66 | 67 | def chord(x, law = 0.05): 68 | ''' 69 | Devuelve un array de longitudes de cuerda. 70 | Entradas: 71 | -x : array de posiciones (0 = raíz, 1 = punta) 72 | -law : describe la forma de la pala, y puede ser: 73 | - un número float: la cuerda es constante, con ese valor en metros 74 | - una lista de la forma ['l', x1, x2 ]: 75 | La cuerda es lineal con la posición, y mide x1 m en la raíz y x2 m en la punta 76 | ''' 77 | c_0 = np.array([1]) 78 | if type(x) == np.ndarray: 79 | c_0 = np.ones_like(x) 80 | 81 | 82 | 83 | 84 | if type(law) == float : 85 | #print('law is float') 86 | c = c_0 * law 87 | elif type(law) == list : 88 | if law[0] == 'l': 89 | c = law[1] + (law[2] - law[1]) * x 90 | else: 91 | c = 'ERROR ' 92 | print('Law not recogniced: ', type(law)) 93 | print(5 * c) 94 | else: 95 | c = 'ERROR ' 96 | print('Law not recogniced: ', type(law)) 97 | print(5 * c) 98 | return c 99 | 100 | 101 | def torsion(x, law = 'c', p = 10): 102 | 103 | ''' 104 | Devuelve un array de torsiones. 105 | Entradas: 106 | -x : array de posiciones (0 = raíz, 1 = punta) 107 | -p : parámetro 108 | -law : describe la forma de la ley de torsiones, y puede ser: 109 | - 'c': distribución de torsión constante = p 110 | - 'l': distribución de torsión lineal, con p[0] en la raíz y p[1] en la punta 111 | - 'h': distribución de torsión hiperbólica, con torsión p en la punta 112 | ''' 113 | 114 | c_0 = np.array([1]) 115 | if type(x) == np.ndarray: 116 | c_0 = np.ones_like(x) 117 | 118 | if law == 'c' : 119 | t = c_0 * p 120 | elif law == 'h' : 121 | 122 | t0 = (p / x) 123 | t = (t0 * (0.5 * (np.sign(90 - t0) + 1)) + 124 | 90 * (0.5 * (np.sign(t0 - 90) + 1)) ) 125 | elif law == 'l': 126 | t = p[0] + (p[1] - p[0]) * x 127 | else: 128 | t = 'ERROR' 129 | return t 130 | 131 | 132 | def integrar(x, y): 133 | step = x[1:] - x[:-1] 134 | y0 = (y[:-1] + y[1:])/2 135 | return np.sum(step * y0) 136 | 137 | def rotor_adim(omega, vz, R, b, 138 | x_min = 0.05, n = 500, 139 | theta0 = 0, tors_param = ['c', 10], 140 | chord_params = 0.05, 141 | rho = 0.0208): 142 | '''Calcula un rotor teniendo en cuenta perdidas de punta de pala''' 143 | 144 | x = np.linspace(x_min, 1, n) 145 | dx = x[1] - x[0] 146 | r = x * R 147 | dr = dx * R 148 | vr = omega * r 149 | c = chord(x, chord_params) 150 | sigma = b * c / (np.pi * R) 151 | 152 | 153 | theta = theta0 + torsion(x, tors_param[0], tors_param[1]) * np.pi / 180 154 | a0 = 2 * np.pi 155 | a = a0 156 | vza = vz / (omega * R) 157 | 158 | #teoría elemento de pala + tubo de corriente: 159 | 160 | 161 | 162 | 163 | for ii in range(5): 164 | ao = a * sigma / 2 165 | via = 0.5 * ( - (vza + ao/4) + np.sqrt((vza + ao/4)**2 + ao *(x * theta - vza))) 166 | vi = omega * R * via 167 | ut = vr 168 | up = -(vz * np.ones_like(x) + vi) 169 | ur = np.sqrt(ut **2 + up ** 2) 170 | fi = np.arctan(up / ut) 171 | alpha = theta + fi 172 | a_1 = a 173 | a = airf_slope( alpha, ur, c, x, rho = rho) 174 | #print(np.mean(a), np.mean(a/a0), np.mean(a/a_1)) 175 | 176 | 177 | cl, cd = airf_aerodata(c, ur, alpha * 180 / np.pi, x, rho = rho)#[0:2] 178 | 179 | 180 | dct = ao * (theta - (vza + via)/x) * x**2 181 | dcpi = - fi * x * dct 182 | dcp0 = sigma * cd * x**3 / 2 183 | 184 | ct = integrar(x, dct) 185 | cpi = integrar(x, dcpi) 186 | cp0 = integrar(x, dcp0) 187 | cp = cpi + cp0 188 | 189 | 190 | #Calculamos el factor B para representar las pérdidas en Punta de Pala: 191 | 192 | B = 1 - np.sqrt(abs(2 * ct)) / b 193 | #print('B = ', B) 194 | 195 | dct = np.where(x < B, dct, 0) 196 | dcpi = np.where(x < B, dcpi, 0) 197 | 198 | ct = integrar(x, dct) 199 | cpi = integrar(x, dcpi) 200 | cp0 = integrar(x, dcp0) 201 | cp = cpi + cp0 202 | 203 | return (ct, cp, vza) 204 | 205 | def densidad(h): 206 | t = 288.15 - 0.0065 * h 207 | rho = 1.225 * (t/288.15) ** (9.8 / (287 * 0.0065) - 1) 208 | return rho 209 | 210 | def calcular_rotor(omega, vz, R, b, 211 | h = 0, 212 | theta0 = 0.174, tors_param = ['h', 14], 213 | chord_params = 0.05): 214 | ''' 215 | Calcula las propiedades de una hélice. 216 | 217 | Argumentos obligatorios: 218 | 219 | - omega: velocidad de giro de la hélice, en rad/s 220 | - vz: velocidad de avance, en m/s 221 | - R : radio de la hélice 222 | - b : número de palas 223 | 224 | Argumentos opcionales: 225 | 226 | - h : altitud de vuelo, en metros sobre el nivel del mar 227 | - theta0 : ángulo de paso colectivo 228 | - tors_param : parámetros de torsión de la hélice: 229 | formato: [ley, p] 230 | p: Parámetro: número o lista 231 | Ley:describe la forma de la ley de torsiones, y puede ser: 232 | - 'c': distribución de torsión constante = p 233 | - 'l': distribución de torsión lineal, con p[0] en la raíz y p[1] en la punta 234 | - 'h': distribución de torsión hiperbólica, con torsión p en la punta 235 | - chord_params : parámetros de distribución de cuerda de la hélice. 236 | Describe la forma de la pala, y puede ser: 237 | - un número float: la cuerda es constante, con ese valor en metros 238 | - una lista de la forma ['l', x1, x2 ]: 239 | La cuerda es lineal con la posición, y mide x1 m en la raíz y x2 m en la punta 240 | Devuelve: 241 | 242 | - T : tracción de la hélice, en Newtons 243 | - P : potencia de la hélice, en Watios 244 | - efic : eficiencia o rendimiento de la hélice (a v=0 es 0 por definición) 245 | - mach_punta : número de mach de las puntas de la hélice 246 | ''' 247 | x_min = 0.05 248 | n = 100 249 | temp = 288.15 - 0.0065 * h 250 | v_son = np.sqrt(1.4 * 8.314 * temp / 0.029) 251 | rho = densidad(h) 252 | sup = np.pi * R**2 253 | ct, cp, vza = rotor_adim(omega, vz, R, b, x_min, n, theta0, tors_param, chord_params, rho) 254 | T = max(0, ct * rho * sup * omega**2 * R**2) 255 | P = max(0, cp * rho * sup * omega**3 * R**3) 256 | if cp > 0: 257 | efic = T * vz / P 258 | else: 259 | efic = 0 260 | v_punta = np.sqrt(vz**2 + (omega * R)**2) 261 | mach_punta = v_punta / v_son 262 | 263 | return T, P, efic, mach_punta 264 | -------------------------------------------------------------------------------- /Ejercicios/El problema de los matrimonios estables/matrimonios_estables.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "![Aeropython Logo](/files/imagenes/aeropython_logo.png)" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "#El problema de los matrimonios estables" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | ">*Dados un cierto número de hombres y mujeres heterosexuales, organízalos por parejas de tal manera que su matrimonio sea estable. Cada persona ha ordenado a las personas del sexo opuesto según su preferencia. Los matrimonios se consideran estables si no es posible encontrar dos personas del sexo opuesto que se atraigan entre sí más que lo que les atraen sus respectivas parejas.*" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "Este problema siempre tiene solución (¡a veces puede tener varias!) y existe un algoritmo, diseñado por David Gale and Lloyd Shapley en 1962, en el que las parejas se ordenan a sí mismas, como un sistema complejo. Mira en qué consiste el algoritmo en este vídeo:" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": null, 34 | "metadata": { 35 | "collapsed": true 36 | }, 37 | "outputs": [], 38 | "source": [ 39 | "from IPython.display import HTML\n", 40 | "HTML('')" 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "metadata": {}, 46 | "source": [ 47 | "Nota: Este ejercicio sigue la nomenclatura y la estructura clásicas de este problema para que resulte intuitivo y fácil de seguir, no pretende ser un modelo real de comportamiento. Desde la organización de la PyConEs y nosotros mismos, queremos fomentar y apoyar la diversidad y la tolerancia en todas las facetas de la sociedad, y respetamos por igual todas las identidades de género y sexualidad." 48 | ] 49 | }, 50 | { 51 | "cell_type": "code", 52 | "execution_count": 1, 53 | "metadata": { 54 | "collapsed": true 55 | }, 56 | "outputs": [], 57 | "source": [ 58 | "import numpy as np\n", 59 | "import random as random" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": 2, 65 | "metadata": { 66 | "collapsed": false 67 | }, 68 | "outputs": [], 69 | "source": [ 70 | "class Woman (object):\n", 71 | " ''' Este es el elemento estático, quien recibe propuestas y elige al mejor'''\n", 72 | " def __init__(self, name):\n", 73 | " \n", 74 | " self.name = name\n", 75 | " self.preferences = {}\n", 76 | " self.preferences_inv = {}\n", 77 | " self.boyfriend = []\n", 78 | " self.candidates = []\n", 79 | " \n", 80 | " def engage(self, man):\n", 81 | " self.boyfriend = man\n", 82 | " man.girlfriend = self\n", 83 | " \n", 84 | " def breakup(self, man):\n", 85 | " self.boyfriend = []\n", 86 | " man.girlfriend = []\n", 87 | " \n", 88 | " \n", 89 | "class Man (object):\n", 90 | " '''Este es el elemento dinámico, que busca a su mejores opciones y se propone'''\n", 91 | " \n", 92 | " def __init__(self, name):\n", 93 | " \n", 94 | " self.name = name\n", 95 | " self.preferences = {}\n", 96 | " self.preferences_inv = {}\n", 97 | " self.girlfriend = []\n", 98 | " self.number_of_proposals = 1\n", 99 | " \n", 100 | " def propose(self, woman):\n", 101 | " woman.candidates += [self]\n", 102 | " self.number_of_proposals += 1" 103 | ] 104 | }, 105 | { 106 | "cell_type": "markdown", 107 | "metadata": {}, 108 | "source": [ 109 | "Ahora creamos nuestra población, y la repartimos en dos listas." 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": 3, 115 | "metadata": { 116 | "collapsed": true 117 | }, 118 | "outputs": [], 119 | "source": [ 120 | "magdalena, elena, ana, julia, marta = Woman('Magdalena'), Woman('Elena'), Woman('Ana'), Woman('Julia'), Woman('Marta')\n", 121 | "carlos, siro, manuel, antonio, javier = Man('Carlos'), Man('Siro'), Man('Manuel'), Man('Antonio'), Man('Javier')\n", 122 | "\n", 123 | "women = [magdalena, elena, ana, julia, marta]\n", 124 | "men =[carlos, siro, manuel, antonio, javier]" 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": 4, 130 | "metadata": { 131 | "collapsed": false 132 | }, 133 | "outputs": [], 134 | "source": [ 135 | "for woman in women:\n", 136 | " #Generamos una lista de preferencias de manera aleatoria\n", 137 | " preferences = [ii for ii in range(1, len(men)+1)]\n", 138 | " random.shuffle(preferences)\n", 139 | " \n", 140 | " #Estas preferencias se almacenan como dos diccionarios\n", 141 | " for index in range(len(men)):\n", 142 | " woman.preferences[preferences[index]] = men[index]\n", 143 | " \n", 144 | " for index in range(1, len(men)+1):\n", 145 | " woman.preferences_inv[woman.preferences.get(index).name] = index\n", 146 | " \n", 147 | "for man in men:\n", 148 | " preferences = [ii for ii in range(1, len(women)+1)]\n", 149 | " random.shuffle(preferences)\n", 150 | " \n", 151 | " for index in range(len(women)):\n", 152 | " man.preferences[preferences[index]] = women[index]\n", 153 | " \n", 154 | " for index in range(1, len(men)+1):\n", 155 | " man.preferences_inv[man.preferences.get(index).name] = index" 156 | ] 157 | }, 158 | { 159 | "cell_type": "code", 160 | "execution_count": 5, 161 | "metadata": { 162 | "collapsed": false 163 | }, 164 | "outputs": [], 165 | "source": [ 166 | "def noche_de_fiesta(man, women):\n", 167 | " \n", 168 | " for woman in women:\n", 169 | " woman.candidates=[]\n", 170 | " \n", 171 | " for man in men:\n", 172 | " if man.girlfriend == []:\n", 173 | " man.propose(man.preferences[man.number_of_proposals])\n", 174 | " \n", 175 | " for woman in women:\n", 176 | " \n", 177 | " if woman.boyfriend == []:\n", 178 | " for ii in range(1, len(men)+1):\n", 179 | " if woman.preferences[ii] in woman.candidates:\n", 180 | " woman.engage(woman.preferences[ii])\n", 181 | " break\n", 182 | " \n", 183 | " elif any (woman.preferences_inv[man.name]>woman.preferences_inv[woman.boyfriend.name] for man in woman.candidates):\n", 184 | " woman.breakup(woman.boyfriend)\n", 185 | " for ii in range(1, len(men)+1):\n", 186 | " if woman.preferences[ii] in woman.candidates:\n", 187 | " woman.engage(woman.preferences[ii])\n", 188 | " break " 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": null, 194 | "metadata": { 195 | "collapsed": false 196 | }, 197 | "outputs": [], 198 | "source": [ 199 | "for dia in range(1, len(men)+2):\n", 200 | " print('Noche ' + str(dia))\n", 201 | " print('-------')\n", 202 | " noche_de_fiesta(men, women)\n", 203 | " for woman in women:\n", 204 | " print(woman.name)\n", 205 | " if woman.candidates != []:\n", 206 | " print(' Candidatos: ', [candidate.name for candidate in woman.candidates])\n", 207 | " if woman.boyfriend != []:\n", 208 | " print(' Novio: ', woman.boyfriend.name)\n", 209 | " print()\n", 210 | " print()" 211 | ] 212 | }, 213 | { 214 | "cell_type": "markdown", 215 | "metadata": {}, 216 | "source": [ 217 | "Este ejercicio parece un poco extraño, ¿no?\n", 218 | "\n", 219 | "Pero... ¿y si te dijera que es un algoritmo muy utilizado diariamente por instituciones de todo el mundo?" 220 | ] 221 | }, 222 | { 223 | "cell_type": "markdown", 224 | "metadata": {}, 225 | "source": [ 226 | "##Te toca trabajar" 227 | ] 228 | }, 229 | { 230 | "cell_type": "markdown", 231 | "metadata": {}, 232 | "source": [ 233 | "Este algoritmo es muy usado para asignación de plazas en oposiciones, y se usa en varios países para repartir a los candidatos en las diferentes plazas según sus resultados y sus preferencias. Modelémoslo!\n", 234 | "\n", 235 | "Crea unas clases nuevas llamadas 'Candidato' y 'Destino' basadas en 'Man' y 'Woman'. Simplemente con esto, ya tenemos un reparto de plazas muy adecuado, pero podemos mejorar nuestro modelo. Te sugiero que intentes los siguientes cambios:\n", 236 | "\n", 237 | "- Los candidatos generan una propiedad aleatoria llamada 'Nota' al ser creados, que los destinos usan para decidir sus preferencias, en vez de un orden aleatorio\n", 238 | "- Los destino tienen una capacidad de varios puestos, e incluso...\n", 239 | "- Cada destino tiene una cantidad diferente de puestos, que se define al crearlo.\n", 240 | "\n", 241 | "Recuerda que es probable que necesites modificar las funciones anteriores (o crear otras nuevas basadas en ellas, si quieres conservar las originales sin tocar como referencia).\n", 242 | "\n", 243 | "Para crear la población, puede que te resulte útil el método append() en el interior de un bucle." 244 | ] 245 | }, 246 | { 247 | "cell_type": "code", 248 | "execution_count": null, 249 | "metadata": { 250 | "collapsed": true 251 | }, 252 | "outputs": [], 253 | "source": [] 254 | }, 255 | { 256 | "cell_type": "code", 257 | "execution_count": null, 258 | "metadata": { 259 | "collapsed": true 260 | }, 261 | "outputs": [], 262 | "source": [] 263 | }, 264 | { 265 | "cell_type": "markdown", 266 | "metadata": {}, 267 | "source": [ 268 | "Carlos Dorado, Aeropython, 20 de Noviembre de 2015" 269 | ] 270 | }, 271 | { 272 | "cell_type": "code", 273 | "execution_count": null, 274 | "metadata": { 275 | "collapsed": true 276 | }, 277 | "outputs": [], 278 | "source": [] 279 | } 280 | ], 281 | "metadata": { 282 | "kernelspec": { 283 | "display_name": "Python 3", 284 | "language": "python", 285 | "name": "python3" 286 | }, 287 | "language_info": { 288 | "codemirror_mode": { 289 | "name": "ipython", 290 | "version": 3 291 | }, 292 | "file_extension": ".py", 293 | "mimetype": "text/x-python", 294 | "name": "python", 295 | "nbconvert_exporter": "python", 296 | "pygments_lexer": "ipython3", 297 | "version": "3.5.2" 298 | } 299 | }, 300 | "nbformat": 4, 301 | "nbformat_minor": 0 302 | } 303 | -------------------------------------------------------------------------------- /Agradecimientos.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "![Aropython_logo](./static/aeropython_name_mini.png)" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "# Simplifica tu vida con sistemas complejos y algoritmos genéticos" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "## Otras personas que han hecho este curso posible" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "### John Henry Holland (1929 – 2015) " 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "\n", 36 | "\n", 37 | "* **Científico**\n", 38 | "* Pionero de los sistemas complejos adaptativos.\n", 39 | "* Padre del algoritmo genético.\n", 40 | "* Profesor de Psicología, Ingeniería Eléctrica y Ciencias de la Computación en la Universidad de Michigan.\n" 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "metadata": {}, 46 | "source": [ 47 | "### Ángel Velázquez" 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": {}, 53 | "source": [ 54 | "\n", 55 | "\n", 56 | "* **Investigador**\n", 57 | "* Profesor en la Universidad Politécnica de Madrid.\n", 58 | "* [Linked-in](https://es.linkedin.com/in/ángel-velázquez-a2779126/en)\n" 59 | ] 60 | }, 61 | { 62 | "cell_type": "markdown", 63 | "metadata": {}, 64 | "source": [ 65 | "### Jean-Louis Dessalles" 66 | ] 67 | }, 68 | { 69 | "cell_type": "markdown", 70 | "metadata": {}, 71 | "source": [ 72 | "\n", 73 | "\n", 74 | "* **Investigador**\n", 75 | "* Profesor en Télécom ParisTech" 76 | ] 77 | }, 78 | { 79 | "cell_type": "markdown", 80 | "metadata": {}, 81 | "source": [ 82 | "### Hiroki Sayama" 83 | ] 84 | }, 85 | { 86 | "cell_type": "markdown", 87 | "metadata": {}, 88 | "source": [ 89 | "\n", 90 | "\n", 91 | "* **Investigador**\n", 92 | "* Profesor en la Universidad de Binghamton." 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | "### Sean Luke" 100 | ] 101 | }, 102 | { 103 | "cell_type": "markdown", 104 | "metadata": {}, 105 | "source": [ 106 | "\n", 107 | "\n", 108 | "* **Investigador**\n", 109 | "* Profesor en la Universidad George Mason." 110 | ] 111 | }, 112 | { 113 | "cell_type": "markdown", 114 | "metadata": {}, 115 | "source": [ 116 | "### Aeropython" 117 | ] 118 | }, 119 | { 120 | "cell_type": "markdown", 121 | "metadata": {}, 122 | "source": [ 123 | "\n", 124 | "\n", 125 | "* **Grupo creado en ¿2013? en la Escuela Técnica Superior de Ingenieros Aeronáuticos**\n", 126 | "* Fundado por [Juan Luis Cano](https://www.linkedin.com/in/juanluiscanor) y [Álex Sáez](https://www.linkedin.com/in/alejandrosaezm) \n", 127 | "* Unas 15 personas forman el núcleo\n", 128 | "* Más de 60 forman parte del [grupo abierto de Telegram](https://telegram.me/AeroPython)\n", 129 | "* Más de 280 seguidores en [Twitter](https://twitter.com/AeroPython)\n", 130 | "* Unos 20 repositorios en [Github](https://github.com/AeroPython)" 131 | ] 132 | }, 133 | { 134 | "cell_type": "markdown", 135 | "metadata": {}, 136 | "source": [ 137 | "---" 138 | ] 139 | }, 140 | { 141 | "cell_type": "markdown", 142 | "metadata": {}, 143 | "source": [ 144 | "Siro Moreno y Carlos Dorado, Aeropython, 7 de Octubre de 2016\n" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": 1, 150 | "metadata": { 151 | "collapsed": false 152 | }, 153 | "outputs": [ 154 | { 155 | "data": { 156 | "text/html": [ 157 | "\n", 158 | "\n", 159 | "\n", 160 | "\n", 283 | "\n" 299 | ], 300 | "text/plain": [ 301 | "" 302 | ] 303 | }, 304 | "execution_count": 1, 305 | "metadata": {}, 306 | "output_type": "execute_result" 307 | } 308 | ], 309 | "source": [ 310 | "# Notebook style\n", 311 | "from IPython.core.display import HTML\n", 312 | "css_file = './static/style.css'\n", 313 | "HTML(open(css_file, \"r\").read())" 314 | ] 315 | }, 316 | { 317 | "cell_type": "code", 318 | "execution_count": null, 319 | "metadata": { 320 | "collapsed": true 321 | }, 322 | "outputs": [], 323 | "source": [] 324 | } 325 | ], 326 | "metadata": { 327 | "kernelspec": { 328 | "display_name": "Python 3", 329 | "language": "python", 330 | "name": "python3" 331 | }, 332 | "language_info": { 333 | "codemirror_mode": { 334 | "name": "ipython", 335 | "version": 3 336 | }, 337 | "file_extension": ".py", 338 | "mimetype": "text/x-python", 339 | "name": "python", 340 | "nbconvert_exporter": "python", 341 | "pygments_lexer": "ipython3", 342 | "version": "3.4.1" 343 | } 344 | }, 345 | "nbformat": 4, 346 | "nbformat_minor": 0 347 | } 348 | -------------------------------------------------------------------------------- /Presentacion ponentes.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "![Aropython_logo](./static/aeropython_name_mini.png)" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "# Simplifica tu vida con sistemas complejos y algoritmos genéticos" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "## ¿Quiénes somos nosotros?" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "### Carlos Dorado Cárdenas" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "\n", 36 | "\n", 37 | "* **Ingeniero Aeronáutico**\n", 38 | "* Trabajando en Dyson\n", 39 | "* [Linked-in](https://www.linkedin.com/in/carlosdoradocardenas)\n", 40 | "* [Github](https://github.com/cdorado)" 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "metadata": {}, 46 | "source": [ 47 | "### Siro Moreno Martín" 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": {}, 53 | "source": [ 54 | "\n", 55 | "\n", 56 | "* **Ingeniero Aeronáutico**\n", 57 | "* Trabajando en el Instituto Nacional de Técnica Aeroespacial \n", 58 | "* [Linked-in](www.linkedin.com/in/siro-moreno-martin)\n", 59 | "* [Github](https://github.com/AunSiro)" 60 | ] 61 | }, 62 | { 63 | "cell_type": "markdown", 64 | "metadata": {}, 65 | "source": [ 66 | "### Aeropython" 67 | ] 68 | }, 69 | { 70 | "cell_type": "markdown", 71 | "metadata": {}, 72 | "source": [ 73 | "\n", 74 | "\n", 75 | "* **Grupo creado en ¿2013? en la Escuela Técnica Superior de Ingenieros Aeronáuticos**\n", 76 | "* Fundado por [Juan Luis Cano](https://www.linkedin.com/in/juanluiscanor) y [Álex Sáez](https://www.linkedin.com/in/alejandrosaezm) \n", 77 | "* Unas 15 personas forman el núcleo\n", 78 | "* Más de 60 forman parte del [grupo abierto de Telegram](https://telegram.me/AeroPython)\n", 79 | "* Más de 280 seguidores en [Twitter](https://twitter.com/AeroPython)\n", 80 | "* Unos 20 repositorios en [Github](https://github.com/AeroPython)" 81 | ] 82 | }, 83 | { 84 | "cell_type": "markdown", 85 | "metadata": {}, 86 | "source": [ 87 | "---" 88 | ] 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "metadata": { 93 | "collapsed": true 94 | }, 95 | "source": [ 96 | "## Sobre la estructura del taller:" 97 | ] 98 | }, 99 | { 100 | "cell_type": "markdown", 101 | "metadata": {}, 102 | "source": [ 103 | "\n", 104 | "* **Bloque A - 45 minutos: Introducción a los Sistemas Complejos**\n", 105 | " * Teoría\n", 106 | " * Qué son los S.C\n", 107 | " * Campos de aplicación\n", 108 | " * Tipos de S.C\n", 109 | " * Ejercicio - Ecuaciones depredador-presa\n", 110 | " * Teoría\n", 111 | " * Conceptos clave\n", 112 | " * Autómatas Celulares\n", 113 | " * Ejercicio - Autómata 1D\n", 114 | " * Demo - El juego de la Vida\n", 115 | " * Teoría - Interés ingenieril de los SC\n", 116 | " * Demo - Algoritmo de Colonia de Hormigas\n", 117 | " * *Dependiendo del tiempo* Demo - ABM: Vecindario de Schelling\n", 118 | " \n", 119 | "* **Bloque B - 45 Minutos: Algoritmos Genéticos y Métodos Heurísticos**\n", 120 | " * Metaheurística\n", 121 | " * Qué es la metaheurística\n", 122 | " * Ejercicios de metaheurística\n", 123 | " * Algoritmos de base: _Random Search_ y _Hill climbing_\n", 124 | " * Dilema exploración-explotación\n", 125 | " * Algoritmos genéticos\n", 126 | " * Qué son los algoritmos genéticos\n", 127 | " * Optimización de funciones y ajuste de parámetros\n", 128 | " * Demo - Optimización de aletas de refrigeración\n", 129 | " * Ejercicio - Optimización de un rotor de helicóptero\n", 130 | " * Demo - Resolución de laberintos\n", 131 | "\n", 132 | "* **Bloque C - 40 Minutos: Ajuste de algoritmos, técnicas y paquetes de Python**\n", 133 | " * Demo - Influencia de parámetros: cambios de fase y caos\n", 134 | " * Teoría - Modelos de Redes\n", 135 | " * Ejercicio - El paquete NetworkX, tipos y propiedades de los grafos\n", 136 | " * Teoría - Modelos basados en Agentes\n", 137 | " * *Dependiendo del tiempo* Demo - ABM: Matrimonios Estables\n", 138 | " * Demo - El script PyCX para prototipado de SC\n", 139 | " * Demo - El paquete DEAP para algoritmos evolutivos\n", 140 | " * *Dependiendo del tiempo* Vídeo - CC - Patrones en reactivos y modelo reacción-difusión\n" 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": null, 146 | "metadata": { 147 | "collapsed": true 148 | }, 149 | "outputs": [], 150 | "source": [] 151 | }, 152 | { 153 | "cell_type": "markdown", 154 | "metadata": {}, 155 | "source": [ 156 | "Siro Moreno y Carlos Dorado, Aeropython, 7 de Octubre de 2016\n" 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": 4, 162 | "metadata": { 163 | "collapsed": false 164 | }, 165 | "outputs": [ 166 | { 167 | "data": { 168 | "text/html": [ 169 | "\n", 170 | "\n", 171 | "\n", 172 | "\n", 295 | "\n" 311 | ], 312 | "text/plain": [ 313 | "" 314 | ] 315 | }, 316 | "execution_count": 4, 317 | "metadata": {}, 318 | "output_type": "execute_result" 319 | } 320 | ], 321 | "source": [ 322 | "# Notebook style\n", 323 | "from IPython.core.display import HTML\n", 324 | "css_file = './static/style.css'\n", 325 | "HTML(open(css_file, \"r\").read())" 326 | ] 327 | }, 328 | { 329 | "cell_type": "code", 330 | "execution_count": null, 331 | "metadata": { 332 | "collapsed": true 333 | }, 334 | "outputs": [], 335 | "source": [] 336 | } 337 | ], 338 | "metadata": { 339 | "kernelspec": { 340 | "display_name": "Python 3", 341 | "language": "python", 342 | "name": "python3" 343 | }, 344 | "language_info": { 345 | "codemirror_mode": { 346 | "name": "ipython", 347 | "version": 3 348 | }, 349 | "file_extension": ".py", 350 | "mimetype": "text/x-python", 351 | "name": "python", 352 | "nbconvert_exporter": "python", 353 | "pygments_lexer": "ipython3", 354 | "version": "3.4.1" 355 | } 356 | }, 357 | "nbformat": 4, 358 | "nbformat_minor": 0 359 | } 360 | -------------------------------------------------------------------------------- /Ejercicios/Hormiguero/ants/_ants.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """Ejercicio del Algoritmo de Colonia de Hormigas 5 | 6 | Taller de la PyConEs 2015: Simplifica tu vida con sistemas complejos y algoritmos genéticos 7 | 8 | Este script contiene las funciones y clases necesarias para ejecutar una simulación del 9 | Algoritmo de colonia de hormigas para optimizar el problema del viajante. 10 | 11 | 12 | Este script usa arrays de numpy, aunque no debería ser difícil para alguien con experiencia 13 | sustituírlos por otras estructuras si es necesario. 14 | 15 | También usa la librería Matplotlib en las funciones que dibujan resultados.""" 16 | 17 | 18 | import numpy as np 19 | import matplotlib.pyplot as plt 20 | 21 | #Primero vamos a definir los dos tipos de objeto que necesitamos: 22 | class Mapa: 23 | '''Este objeto contiene el mapa de las ciudades que se van a visitar, 24 | con todos los datos necesarios (como las feromonas y distancias entre ciudades) 25 | y las hormigas que lo recorren''' 26 | def __init__ (self, n_ciud = 10, mapsize = 10): 27 | self.n_ciud = n_ciud 28 | self.mapsize = mapsize 29 | self.ciudades = mapsize * np.random.rand(n_ciud, 2) 30 | self.distances = np.zeros([n_ciud, n_ciud]) 31 | for i in range(n_ciud): 32 | for j in range(i+1, n_ciud): 33 | vector = self.ciudades[i,:] - self.ciudades[j,:] 34 | modulo = np.linalg.norm(vector) 35 | self.distances[i,j] = modulo 36 | self.distances[j,i] = modulo 37 | self.feromap = np.zeros_like(self.distances) 38 | self.feromultiplier = 1 39 | self.conjunto_analisis = [] 40 | self.pathdata = [] 41 | self.bestpath = [0,1] 42 | self.bestpathlenght = n_ciud * mapsize 43 | 44 | def show_distances_matrix(self): 45 | '''Muestra la matriz de distancias en código de colores''' 46 | plt.matshow(self.distances) 47 | plt.title('Matriz de distancias entre ciudades') 48 | 49 | def show_feromones_matrix(self): 50 | '''Muestra la matriz de feromonas en código de colores''' 51 | plt.matshow(self.feromap) 52 | plt.title('Matriz de feromonas entre ciudades') 53 | 54 | def draw_distances(self): 55 | '''Dibuja un mapa con las ciudades, unidas entre sí por líneas que son más gruesas 56 | cuanto más cercanas son. Es una manera gráfica de comprobar la matriz de distancias.''' 57 | plt.figure(None, figsize=(8,8)) 58 | plt.scatter(self.ciudades[:,0], self.ciudades[:,1], s = 100, c = '#5599FF',zorder=2) 59 | for i in range(self.n_ciud): 60 | for j in range(i+1, self.n_ciud): 61 | path = np.zeros([2,2]) 62 | path[0,:] = self.ciudades[i,:] 63 | path[1,:] = self.ciudades[j,:] 64 | dist = self.distances[i,j] 65 | thickness = 7 - dist * (7 / self.mapsize) 66 | plt.plot(path[:,0], path[:,1],'#88AA22', linewidth=thickness,zorder=1) 67 | plt.title('Mapa de ciudades con sus distancias') 68 | 69 | def draw_feromones(self, rescale_lines = True): 70 | '''Dibuja un mapa con las ciudades, unidas entre sí por líneas que son más gruesas 71 | cuanto más feromonas contiene la ruta que las une. Es una manera gráfica 72 | de comprobar la matriz de feromonas.''' 73 | plt.figure(None, figsize=(8,8)) 74 | plt.scatter(self.ciudades[:,0], self.ciudades[:,1], s = 100, c = '#5599FF',zorder=2) 75 | if rescale_lines: 76 | maxfer = np.max(self.feromap) 77 | for i in range(self.n_ciud): 78 | for j in range(i+1, self.n_ciud): 79 | path = np.zeros([2,2]) 80 | path[0,:] = self.ciudades[i,:] 81 | path[1,:] = self.ciudades[j,:] 82 | if rescale_lines: 83 | fer = self.feromap[i,j] 84 | if maxfer > 0: 85 | fer *= 7/maxfer 86 | 87 | else: 88 | fer = self.feromap[i,j] 89 | 90 | plt.plot(path[:,0], path[:,1],'#DD2222', linewidth=fer,zorder=1) 91 | plt.title('Mapa de ciudades con sus rastros de feromonas') 92 | 93 | def draw_best_path(self): 94 | '''Dibuja un mapa con las ciudades, unidas entre sí por la mejor ruta encontrada hasta el momento.''' 95 | plt.figure(None, figsize=(8,8)) 96 | plt.scatter(self.ciudades[:,0], self.ciudades[:,1], s = 100, c = '#5599FF',zorder=2) 97 | ruta = self.ciudades[[self.bestpath]] 98 | plt.plot(ruta[:,0], ruta[:,1],'#2222AA', linewidth=8,zorder=1) 99 | plt.title('Mapa de ciudades con mejor ruta encontrada') 100 | 101 | def draw_results(self, relative_scale = False): 102 | '''Dibuja la longitud máxima, mínima y media de los caminos que siguen las hormigas, 103 | y la longitud mínima que el algoritmo ha encontrado''' 104 | plt.figure(None, figsize=(8,5)) 105 | patharray = np.array(self.pathdata) 106 | for i in range(3): 107 | plt.plot(patharray[:,i]) 108 | longx = len(patharray[:,0]) 109 | plt.plot([0, longx], [self.bestpathlenght, self.bestpathlenght]) 110 | plt.title('Longitud máxima, mínima, media y mejor camino encontrado') 111 | if not relative_scale : plt.ylim(0) 112 | 113 | def draw_best_results(self, relative_scale = False): 114 | '''Dibuja la longitud mínima de los caminos que siguen las hormigas, 115 | para todas las veces que el algoritmo se ha ejecutado, 116 | y la longitud mínima que el algoritmo ha encontrado''' 117 | plt.figure(None, figsize=(8,5)) 118 | longx = 0 119 | for i in range(len(self.conjunto_analisis)): 120 | patharray = np.array(self.conjunto_analisis[i]) 121 | plt.plot(patharray[:,1]) 122 | longx = max(longx, len(patharray[:,0])) 123 | 124 | patharray = np.array(self.pathdata) 125 | longx = max(longx, len(patharray[:,0])) 126 | plt.plot(patharray[:,1]) 127 | 128 | plt.plot([0, longx], [self.bestpathlenght, self.bestpathlenght]) 129 | plt.title('Longitud mínima para cada ejecución y mejor camino encontrado') 130 | if not relative_scale : plt.ylim(0) 131 | 132 | def swarm_create(self, n_ant = 10): 133 | '''Crea una población de hormigas en el mapa''' 134 | self.lista_hormigas = [] 135 | for i in range(n_ant): 136 | nueva = Hormiga(self) 137 | self.lista_hormigas.append(nueva) 138 | del(nueva) 139 | 140 | def swarm_show(self): 141 | '''Dibuja un mapa con las ciudades y las hormigas. 142 | Es una manera gráfica de comprobar dónde se encuentran.''' 143 | plt.figure(None, figsize=(8,8)) 144 | plt.scatter(self.ciudades[:,0], self.ciudades[:,1], s = 100, c = '#5599FF') 145 | ant_pos = np.zeros([len(self.lista_hormigas), 2]) 146 | for i in range(len(self.lista_hormigas)): 147 | hormiga = self.lista_hormigas[i] 148 | city = hormiga.position 149 | exact_position = self.ciudades[city,:] 150 | aprox_position = exact_position + 0.03 *self.mapsize * (np.random.rand(2) - 0.5) 151 | #print(exact_position) 152 | #print(aprox_position) 153 | ant_pos[i,:] = aprox_position 154 | plt.scatter(ant_pos[:,0], ant_pos[:,1], s = 5, c = 'k') 155 | plt.title('Mapa de ciudades y hormigas') 156 | 157 | def feromone_reset(self): 158 | '''Devuelve a 0 el mapa de feromonas para repetir el análisis 159 | de un mapa dado. 160 | Los datos alcanzados hasta ahora, se guardarán para posterior consulta.''' 161 | self.feromap = np.zeros_like(self.distances) 162 | self.conjunto_analisis.append(self.pathdata) 163 | self.pathdata = [] 164 | 165 | def feromone_fine_tune(self): 166 | '''Permite controlar en detalle la cantidad de feromonas que se evaporan cada turno. 167 | Un factor mayor aumenta la cantidad de feromonas, y viceversa. 168 | Por defecto, el factor = 1.''' 169 | ok = False 170 | while not ok: 171 | x = input('introduzca un valor, p.ej. 1: ') 172 | try: 173 | x = float(x) 174 | ok = True 175 | except: 176 | print('Valor incorrecto') 177 | 178 | print('Valor cambiado') 179 | self.feromultiplier = x 180 | 181 | def swarm_delete(self): 182 | '''Elimina a todas las hormigas del mapa''' 183 | del(self.lista_hormigas) 184 | self.lista_hormigas = [] 185 | 186 | def swarm_generation(self): 187 | '''Realiza una generación completa de hormigas: 188 | 1. Las mueve paso a paso hasta completar la ruta 189 | 2. Analiza los resultados 190 | 3. Deposita las feromonas 191 | 4. Elimina las hormigas viejas y crea una nueva población 192 | 5. Evapora las feromonas''' 193 | n_ant = len(self.lista_hormigas) 194 | self.pathlens = [] 195 | for i in range(self.n_ciud - 1): 196 | for hormiga in self.lista_hormigas: 197 | hormiga.journey_step() 198 | for hormiga in self.lista_hormigas: 199 | hormiga.back_home() 200 | length = hormiga.calc_route_length() 201 | self.pathlens.append(length) 202 | if length < self.bestpathlenght : 203 | self.bestpathlenght = length 204 | self.bestpath = hormiga.route 205 | hormiga.feromone_spray() 206 | 207 | maxpath = max(self.pathlens) 208 | minpath = min(self.pathlens) 209 | meanpath = np.mean(self.pathlens) 210 | self.pathdata.append([maxpath, minpath, meanpath]) 211 | self.swarm_delete() 212 | self.swarm_create(n_ant) 213 | self.feromap /= (3 / self.feromultiplier) 214 | self.feromap -=0.5 215 | self.feromap = np.where(self.feromap>0, self.feromap, np.zeros_like(self.feromap)) 216 | 217 | 218 | class Hormiga: 219 | '''Estas hormigas recorren el mapa acumulando información 220 | sobre la longitud del viaje''' 221 | def __init__(self, mapa): 222 | self.mapa = mapa 223 | self.city_list = list(range(mapa.n_ciud)) 224 | self.start = self.city_list.pop(np.random.randint(mapa.n_ciud)) 225 | self.position = self.start 226 | self.route = [self.start,] 227 | self.distance_weight = 0.2 228 | 229 | def journey_step(self): 230 | '''Avanza a la siguiente ciudad''' 231 | all_dist = self.mapa.distances[self.position, :] 232 | posible_dist = all_dist[self.city_list] 233 | all_ferom = self.mapa.feromap[self.position, :] 234 | posible_ferom = all_ferom[self.city_list] 235 | probabilities = (self.distance_weight / (0.1 + posible_dist) + 236 | (1 - self.distance_weight) * (0.1 + posible_ferom)) 237 | probabilities = probabilities / np.sum(probabilities) 238 | indexes = np.arange(len(self.city_list)) 239 | new_city_index = np.random.choice(indexes, 1, p = probabilities)[0] 240 | self.position = self.city_list.pop(new_city_index) 241 | self.route.append(self.position) 242 | 243 | def back_home(self): 244 | '''Devuelve a la hormiga a su ciudad inicial tras recorrer todo el mapa''' 245 | self.position = self.start 246 | self.route.append(self.start) 247 | 248 | def calc_route_length(self): 249 | '''Calcula la longitud de la ruta de la hormiga''' 250 | self.route_length = 0 251 | for i in range(1, len(self.route)): 252 | city1 = self.route[i-1] 253 | city2 = self.route[i] 254 | dist = self.mapa.distances[city1, city2] 255 | self.route_length += dist 256 | return self.route_length 257 | 258 | def feromone_spray(self): 259 | '''Deposita sobre la ruta recorrida una cantidad de feromonas 260 | que depende de la longitud del viaje''' 261 | feromone_amount = (2 * self.mapa.n_ciud * self.mapa.mapsize)/self.route_length**2 262 | for i in range(1, len(self.route)): 263 | city1 = self.route[i-1] 264 | city2 = self.route[i] 265 | self.mapa.feromap[city1, city2] += feromone_amount 266 | self.mapa.feromap[city2, city1] += feromone_amount 267 | 268 | #Sólo necesitamos estos objetos para ejecutar nuestras simulaciones 269 | 270 | 271 | 272 | 273 | #Ejemplo: 274 | if __name__ == '__main__': 275 | 276 | map1 = ants.Mapa(8) 277 | 278 | 279 | -------------------------------------------------------------------------------- /Ejercicios/Laberinto/laberinto/laberinto.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """Ejercicio del Algoritmo de Colonia de Hormigas 5 | 6 | Taller de la PyConEs 2015: Simplifica tu vida con sistemas complejos y algoritmos genéticos 7 | 8 | Este script contiene las funciones y clases necesarias para el ejercicio del laberinto. 9 | 10 | 11 | Este script usa arrays de numpy, aunque no debería ser difícil para alguien con experiencia 12 | sustituírlos por otras estructuras si es necesario. 13 | 14 | También usa la librería Matplotlib en las funciones que dibujan resultados.""" 15 | 16 | 17 | import numpy as np 18 | import matplotlib.pyplot as plt 19 | try: 20 | import laberinto.algen as ag 21 | except: 22 | try: 23 | import Ejercicios.Laberinto.laberinto.algen as ag 24 | except: 25 | print('Problema al cargar el módulo') 26 | 27 | #Primero vamos a definir los dos tipos de objeto que necesitamos: 28 | class Map(): 29 | def __init__(self, max_steps = 50, veneno = 0) : 30 | 31 | self.max_steps = max_steps 32 | self.veneno = veneno 33 | self.list_caminos = [] 34 | self.history = [] 35 | self.bestpath = None 36 | self.bestscore = -10E8 37 | self.dict_genes = {} 38 | for i in range (10): 39 | for j in range (10): 40 | self.dict_genes[i,j] = 2 41 | #------ -- Matrices del Mapa: --------- 42 | #Esta matriz describe las fronteras de cada casilla con cada número en binario: 43 | #primer dígito binario: frontera superior 44 | #segundo dígito binario: frontera derecha 45 | #tercer dígito binario: frontera inferior 46 | #cuarto dígito binario: frontera izquierda 47 | #Para cada dígito: 1 = frontera cerrada, 0 = frontera abierta 48 | self.grid = np.array([ 49 | [9, 3, 9, 5, 5, 5, 5, 5, 5, 3 ], 50 | [10, 12, 6, 9, 1, 5, 5, 3, 9, 6 ], 51 | [10, 9, 3, 10, 10, 9, 3, 10, 10, 11], 52 | [10, 10, 10, 10, 10, 12, 6, 10, 8, 2 ], 53 | [8, 2, 8, 6, 12, 5, 5, 6, 10, 12], 54 | [10, 10, 12, 1, 5, 5, 1, 3, 12, 3 ], 55 | [10, 12, 3, 10, 9, 5, 6, 12, 3, 10], 56 | [8, 5, 6, 10, 10, 9, 3, 11, 10, 10], 57 | [10, 9, 3, 10, 12, 6, 12, 2, 10, 10], 58 | [12, 6, 12, 4, 5, 5, 5, 6, 12, 6 ] 59 | ]) 60 | 61 | #Esta matriz simplemente es un damero que sirve para pintarlo bonito 62 | self.back = np.zeros([10,10]) 63 | self.back[::2, ::2] = 1 64 | self.back[1::2, 1::2] = 1 65 | #En esta matriz guardaremos feromonas: 66 | self.feromap = np.zeros((10,10)) 67 | 68 | def draw_tablero(self): 69 | '''Dibuja el laberinto''' 70 | plt.figure(1, figsize=(10,10)) 71 | plt.matshow(self.back, fignum= 1, cmap=plt.cm.Oranges, alpha = 0.4) 72 | #plt.contourf(xx, yy, back, np.linspace(-1, 2, 3), cmap=plt.cm.Blues) 73 | plt.xlim(-1,10) 74 | plt.ylim(-1,10) 75 | x = list(range(10)) 76 | y = x 77 | for i in x: 78 | for j in y: 79 | if self.grid[j,i] & 1 : 80 | xx = i + np.array([-0.5, 0.5]) 81 | yy = j + np.array([-0.5,-0.5]) 82 | plt.plot(xx,yy, 'k', linewidth=3) 83 | if self.grid[j,i] & 2 : 84 | xx = i + np.array([ 0.5, 0.5]) 85 | yy = j + np.array([-0.5, 0.5]) 86 | plt.plot(xx,yy, 'k', linewidth=3) 87 | if self.grid[j,i] & 4 : 88 | xx = i + np.array([-0.5, 0.5]) 89 | yy = j + np.array([ 0.5, 0.5]) 90 | plt.plot(xx,yy, 'k', linewidth=3) 91 | if self.grid[j,i] & 8 : 92 | xx = i + np.array([-0.5,-0.5]) 93 | yy = j + np.array([-0.5, 0.5]) 94 | plt.plot(xx,yy, 'k', linewidth=3) 95 | plt.gca().invert_yaxis() 96 | def create_camino(self): 97 | '''Crea un nuevo camino aleatorio''' 98 | self.list_caminos.append(Camino(False, [self])) 99 | 100 | def statistics(self): 101 | '''Analiza los valores de la puntuación de la población''' 102 | scores = [] 103 | for j in self.list_caminos : 104 | scores.append(j.fitness) 105 | if j.fitness > self.bestscore: 106 | self.bestscore = j.fitness 107 | self.bestpath = j 108 | self.history.append([min(scores), sum(scores)/len(scores), max(scores)]) 109 | 110 | def draw_history(self): 111 | '''Dibuja las gráficas de evolución de la puntuación''' 112 | plt.figure(None, figsize=(10, 8)) 113 | history = np.array(self.history) 114 | for i in range(3): 115 | plt.plot(history[:, i]) 116 | plt.title('Puntuación máxima, media y mínima para cada generación') 117 | 118 | def draw_best(self): 119 | '''Dibuja el mejor camino encontrado. 120 | Es necesario pintar el tablero por separado''' 121 | self.bestpath.draw_path(alpha = 0.5, c = 'b', w = 4) 122 | 123 | def draw_poison(self): 124 | '''Dibuja las toxinas o feromonas del mapa. 125 | Es necesario pintar el tablero por separado''' 126 | if self.veneno != 0: 127 | maxpoison = np.max(self.feromap) 128 | for i in range(10): 129 | for j in range(10): 130 | poison = 0.8 * self.feromap[j,i] / maxpoison 131 | plt.plot(i , j, 'o', color = 'g', alpha = poison, markersize=40) 132 | 133 | def reload_poison(self): 134 | '''Actualiza las feromonas y el valor de la aptitud de las soluciones''' 135 | self.bestpath = None 136 | self.bestscore = -10E8 137 | self.feromap /=2 138 | for i in self.list_caminos: 139 | i.deploy_poison() 140 | for i in self.list_caminos: 141 | calculate_fitness(i) 142 | 143 | 144 | class Camino(): 145 | '''Este objeto contiene una disposición dada de direcciones sobre el mapa, 146 | con la que se puede construir un camino''' 147 | def __init__(self, genome = False, opciones = False): 148 | self.poison = 0 149 | if not opciones: 150 | self.mapa = None 151 | else: 152 | self.mapa = opciones[0] 153 | self.dict_genes = {} 154 | for i in range (10): 155 | for j in range (10): 156 | self.dict_genes[i,j] = 2 157 | 158 | if not genome: 159 | self.genome = np.random.randint(0,2,200) 160 | else: 161 | self.genome = genome 162 | 163 | 164 | def draw_directions(self): 165 | '''Dibuja el tablero y a continuación, dibuja sobre él 166 | el mapa de direcciones''' 167 | self.mapa.draw_tablero() 168 | x = list(range(10)) 169 | y = x 170 | for i in x: 171 | for j in y: 172 | 173 | if self.directions[j ,i] == 0: 174 | plt.arrow(i, j + 0.4, 0, -0.6, head_width=0.1, head_length=0.2, fc='b', ec='b') 175 | if self.directions[j ,i] == 1: 176 | plt.arrow(i - 0.4, j, 0.6, 0, head_width=0.1, head_length=0.2, fc='b', ec='b') 177 | if self.directions[j ,i] == 2: 178 | plt.arrow(i, j - 0.4, 0, 0.6, head_width=0.1, head_length=0.2, fc='b', ec='b') 179 | if self.directions[j ,i] == 3: 180 | plt.arrow(i + 0.4, j, -0.6, 0, head_width=0.1, head_length=0.2, fc='b', ec='b') 181 | 182 | #-- Funciones para calcular el camino 183 | 184 | def move(self, row, col, direction): 185 | '''Intenta moverse a la siguiente casilla''' 186 | grid = self.mapa.grid 187 | d = 2 ** direction 188 | 189 | if not grid[row, col] & d: 190 | 191 | if direction == 0: 192 | return row -1, col 193 | elif direction == 1: 194 | return row , col+1 195 | elif direction == 2: 196 | return row +1, col 197 | elif direction == 3: 198 | return row , col-1 199 | else: 200 | return None 201 | 202 | def step(self, row, col, direction, path): 203 | '''Intenta moverse a la siguiente casilla, si no lo consigue 204 | (porque choca con una pared), intenta moverse en otra dirección. 205 | Si la segunda vez tampoco lo consigue, se queda quieto. 206 | 207 | Devuelve información sobre si ha chocado o si ha vuelto a la casilla en la que estaba 208 | en el paso anterior''' 209 | wall = False 210 | u_turn = False 211 | newpos = self.move(row, col, direction) 212 | if newpos == None: 213 | wall = True 214 | new_d = np.random.randint(0,4) 215 | newpos = self.move(row, col, new_d) 216 | 217 | if newpos != None and 0<= col <=9: 218 | row,col = newpos 219 | if len(path) >=2 and [row, col] == path[-2]: 220 | u_turn = True 221 | return row, col, wall, u_turn 222 | 223 | def get_path(self): 224 | '''Calcula el camino a partir del mapa de direcciones''' 225 | 226 | max_steps = self.mapa.max_steps 227 | path = [[4,0]] 228 | wall_count = 0 229 | u_turn_count = 0 230 | for nstep in range(max_steps): 231 | #print('step:', nstep, end=' ') 232 | row, col = path[nstep] 233 | row, col, wall, u_turn = self.step(row, col, self.directions[row, col], path) 234 | wall_count += wall 235 | u_turn_count += u_turn 236 | path.append([row, col]) 237 | if [row,col] == [4, 10]: 238 | break 239 | 240 | self.path, self.wall_count, self.u_turn_count = np.array(path), wall_count, u_turn_count 241 | def deploy_poison(self): 242 | '''Deposita feromonas negativas en las casillas que ha visitado''' 243 | if self.mapa.veneno != 0 : 244 | for i in range(self.path.shape[0]): 245 | row = self.path[i, 0] 246 | col = self.path[i, 1] 247 | if col < 10: 248 | self.poison += self.mapa.feromap[row,col] 249 | self.mapa.feromap[row,col] += 0.1 * self.mapa.veneno 250 | 251 | def draw_path(self, alpha = 0.5, c = 'r', w = 8): 252 | '''Dibuja su camino sobre el mapa. 253 | Es necesario pintar el tablero por separado''' 254 | plt.plot(self.path[:,1], self.path[:,0], c, linewidth = w, alpha = alpha) 255 | 256 | 257 | 258 | def calculate_performances(individual): 259 | '''Calcula las performances de un individuo: 260 | En este caso, el camino a partir del mapa de direcciones. 261 | En este paso también se depositan las feromonas''' 262 | 263 | individual.directions = np.zeros([10,10], dtype=np.int) 264 | for i in range (10): 265 | for j in range (10): 266 | individual.directions[i,j] = individual.traits[(i,j)] 267 | individual.get_path() 268 | individual.deploy_poison() 269 | 270 | def calculate_fitness(individual): 271 | '''Calcula la aptitud de un individuo''' 272 | path, wall_count, u_turn_count = individual.path, individual.wall_count, individual.u_turn_count 273 | poison = individual.poison 274 | max_steps = individual.mapa.max_steps 275 | endx = path[-1,1] 276 | victory = max_steps + 1 - len(path) # >0 si ha llegado al final, mayor cuanto más corto sea el camino 277 | individual.fitness = endx * 4 - 2 * wall_count - 3 * u_turn_count - 0.03 * poison + victory * 5 278 | 279 | def avanzar(mapa, n = 100, 280 | max_pop = 100, min_pop = 10, 281 | reproduction_rate = 8, mutation_rate = 0.05): 282 | '''Efectua una cantidad n de generaciones ''' 283 | for i in range(n): 284 | print(i+1, end='·') 285 | ag.immigration(mapa.list_caminos, max_pop, 286 | calculate_performances, calculate_fitness, 287 | mapa.dict_genes, Camino, mapa) 288 | ag.tournament(mapa.list_caminos, min_pop) 289 | mapa.reload_poison() 290 | ag.crossover(mapa.list_caminos, reproduction_rate, mutation_rate, 291 | calculate_performances, calculate_fitness, 292 | mapa.dict_genes, Camino, mapa) 293 | mapa.statistics() 294 | 295 | def draw_all(mapa): 296 | '''Dibuja el mapa con todas las soluciones de la generación actual, 297 | las feromonas del mapa, y gráficas de evolución.''' 298 | mapa.bestpath.draw_directions() 299 | mapa.draw_poison() 300 | for x in mapa.list_caminos: 301 | x.draw_path(0.1) 302 | 303 | mapa.draw_best() 304 | mapa.draw_history() 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | -------------------------------------------------------------------------------- /Ejercicios/Laberinto (resumido)/laberinto/laberinto.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """Ejercicio del Algoritmo de Colonia de Hormigas 5 | 6 | Taller de la PyConEs 2015: Simplifica tu vida con sistemas complejos y algoritmos genéticos 7 | 8 | Este script contiene las funciones y clases necesarias para el ejercicio del laberinto. 9 | 10 | 11 | Este script usa arrays de numpy, aunque no debería ser difícil para alguien con experiencia 12 | sustituírlos por otras estructuras si es necesario. 13 | 14 | También usa la librería Matplotlib en las funciones que dibujan resultados.""" 15 | 16 | 17 | import numpy as np 18 | import matplotlib.pyplot as plt 19 | try: 20 | import laberinto.algen as ag 21 | except: 22 | try: 23 | import Ejercicios.Laberinto.laberinto.algen as ag 24 | except: 25 | print('Problema al cargar el módulo') 26 | 27 | #Primero vamos a definir los dos tipos de objeto que necesitamos: 28 | class Map(): 29 | def __init__(self, max_steps = 50, veneno = 0) : 30 | 31 | self.max_steps = max_steps 32 | self.veneno = veneno 33 | self.list_caminos = [] 34 | self.history = [] 35 | self.bestpath = None 36 | self.bestscore = -10E8 37 | self.dict_genes = {} 38 | for i in range (10): 39 | for j in range (10): 40 | self.dict_genes[i,j] = 2 41 | #------ -- Matrices del Mapa: --------- 42 | #Esta matriz describe las fronteras de cada casilla con cada número en binario: 43 | #primer dígito binario: frontera superior 44 | #segundo dígito binario: frontera derecha 45 | #tercer dígito binario: frontera inferior 46 | #cuarto dígito binario: frontera izquierda 47 | #Para cada dígito: 1 = frontera cerrada, 0 = frontera abierta 48 | self.grid = np.array([ 49 | [9, 3, 9, 5, 5, 5, 5, 5, 5, 3 ], 50 | [10, 12, 6, 9, 1, 5, 5, 3, 9, 6 ], 51 | [10, 9, 3, 10, 10, 9, 3, 10, 10, 11], 52 | [10, 10, 10, 10, 10, 12, 6, 10, 8, 2 ], 53 | [8, 2, 8, 6, 12, 5, 5, 6, 10, 12], 54 | [10, 10, 12, 1, 5, 5, 1, 3, 12, 3 ], 55 | [10, 12, 3, 10, 9, 5, 6, 12, 3, 10], 56 | [8, 5, 6, 10, 10, 9, 3, 11, 10, 10], 57 | [10, 9, 3, 10, 12, 6, 12, 2, 10, 10], 58 | [12, 6, 12, 4, 5, 5, 5, 6, 12, 6 ] 59 | ]) 60 | 61 | #Esta matriz simplemente es un damero que sirve para pintarlo bonito 62 | self.back = np.zeros([10,10]) 63 | self.back[::2, ::2] = 1 64 | self.back[1::2, 1::2] = 1 65 | #En esta matriz guardaremos feromonas: 66 | self.feromap = np.zeros((10,10)) 67 | 68 | def draw_tablero(self): 69 | '''Dibuja el laberinto''' 70 | plt.figure(1, figsize=(10,10)) 71 | plt.matshow(self.back, fignum= 1, cmap=plt.cm.Oranges, alpha = 0.4) 72 | #plt.contourf(xx, yy, back, np.linspace(-1, 2, 3), cmap=plt.cm.Blues) 73 | plt.xlim(-1,10) 74 | plt.ylim(-1,10) 75 | x = list(range(10)) 76 | y = x 77 | for i in x: 78 | for j in y: 79 | if self.grid[j,i] & 1 : 80 | xx = i + np.array([-0.5, 0.5]) 81 | yy = j + np.array([-0.5,-0.5]) 82 | plt.plot(xx,yy, 'k', linewidth=3) 83 | if self.grid[j,i] & 2 : 84 | xx = i + np.array([ 0.5, 0.5]) 85 | yy = j + np.array([-0.5, 0.5]) 86 | plt.plot(xx,yy, 'k', linewidth=3) 87 | if self.grid[j,i] & 4 : 88 | xx = i + np.array([-0.5, 0.5]) 89 | yy = j + np.array([ 0.5, 0.5]) 90 | plt.plot(xx,yy, 'k', linewidth=3) 91 | if self.grid[j,i] & 8 : 92 | xx = i + np.array([-0.5,-0.5]) 93 | yy = j + np.array([-0.5, 0.5]) 94 | plt.plot(xx,yy, 'k', linewidth=3) 95 | plt.gca().invert_yaxis() 96 | def create_camino(self): 97 | '''Crea un nuevo camino aleatorio''' 98 | self.list_caminos.append(Camino(False, [self])) 99 | 100 | def statistics(self): 101 | '''Analiza los valores de la puntuación de la población''' 102 | scores = [] 103 | for j in self.list_caminos : 104 | scores.append(j.fitness) 105 | if j.fitness > self.bestscore: 106 | self.bestscore = j.fitness 107 | self.bestpath = j 108 | self.history.append([min(scores), sum(scores)/len(scores), max(scores)]) 109 | 110 | def draw_history(self): 111 | '''Dibuja las gráficas de evolución de la puntuación''' 112 | plt.figure(None, figsize=(10, 8)) 113 | history = np.array(self.history) 114 | for i in range(3): 115 | plt.plot(history[:, i]) 116 | plt.title('Puntuación máxima, media y mínima para cada generación') 117 | 118 | def draw_best(self): 119 | '''Dibuja el mejor camino encontrado. 120 | Es necesario pintar el tablero por separado''' 121 | self.bestpath.draw_path(alpha = 0.5, c = 'b', w = 4) 122 | 123 | def draw_poison(self): 124 | '''Dibuja las toxinas o feromonas del mapa. 125 | Es necesario pintar el tablero por separado''' 126 | if self.veneno != 0: 127 | maxpoison = np.max(self.feromap) 128 | for i in range(10): 129 | for j in range(10): 130 | poison = 0.8 * self.feromap[j,i] / maxpoison 131 | plt.plot(i , j, 'o', color = 'g', alpha = poison, markersize=40) 132 | 133 | def reload_poison(self): 134 | '''Actualiza las feromonas y el valor de la aptitud de las soluciones''' 135 | self.bestpath = None 136 | self.bestscore = -10E8 137 | self.feromap /=2 138 | for i in self.list_caminos: 139 | i.deploy_poison() 140 | for i in self.list_caminos: 141 | calculate_fitness(i) 142 | 143 | 144 | class Camino(): 145 | '''Este objeto contiene una disposición dada de direcciones sobre el mapa, 146 | con la que se puede construir un camino''' 147 | def __init__(self, genome = False, opciones = False): 148 | self.poison = 0 149 | if not opciones: 150 | self.mapa = None 151 | else: 152 | self.mapa = opciones[0] 153 | self.dict_genes = {} 154 | for i in range (10): 155 | for j in range (10): 156 | self.dict_genes[i,j] = 2 157 | 158 | if not genome: 159 | self.genome = np.random.randint(0,2,200) 160 | else: 161 | self.genome = genome 162 | 163 | 164 | def draw_directions(self): 165 | '''Dibuja el tablero y a continuación, dibuja sobre él 166 | el mapa de direcciones''' 167 | self.mapa.draw_tablero() 168 | x = list(range(10)) 169 | y = x 170 | for i in x: 171 | for j in y: 172 | 173 | if self.directions[j ,i] == 0: 174 | plt.arrow(i, j + 0.4, 0, -0.6, head_width=0.1, head_length=0.2, fc='b', ec='b') 175 | if self.directions[j ,i] == 1: 176 | plt.arrow(i - 0.4, j, 0.6, 0, head_width=0.1, head_length=0.2, fc='b', ec='b') 177 | if self.directions[j ,i] == 2: 178 | plt.arrow(i, j - 0.4, 0, 0.6, head_width=0.1, head_length=0.2, fc='b', ec='b') 179 | if self.directions[j ,i] == 3: 180 | plt.arrow(i + 0.4, j, -0.6, 0, head_width=0.1, head_length=0.2, fc='b', ec='b') 181 | 182 | #-- Funciones para calcular el camino 183 | 184 | def move(self, row, col, direction): 185 | '''Intenta moverse a la siguiente casilla''' 186 | grid = self.mapa.grid 187 | d = 2 ** direction 188 | 189 | if not grid[row, col] & d: 190 | 191 | if direction == 0: 192 | return row -1, col 193 | elif direction == 1: 194 | return row , col+1 195 | elif direction == 2: 196 | return row +1, col 197 | elif direction == 3: 198 | return row , col-1 199 | else: 200 | return None 201 | 202 | def step(self, row, col, direction, path): 203 | '''Intenta moverse a la siguiente casilla, si no lo consigue 204 | (porque choca con una pared), intenta moverse en otra dirección. 205 | Si la segunda vez tampoco lo consigue, se queda quieto. 206 | 207 | Devuelve información sobre si ha chocado o si ha vuelto a la casilla en la que estaba 208 | en el paso anterior''' 209 | wall = False 210 | u_turn = False 211 | newpos = self.move(row, col, direction) 212 | if newpos == None: 213 | wall = True 214 | new_d = np.random.randint(0,4) 215 | newpos = self.move(row, col, new_d) 216 | 217 | if newpos != None and 0<= col <=9: 218 | row,col = newpos 219 | if len(path) >=2 and [row, col] == path[-2]: 220 | u_turn = True 221 | return row, col, wall, u_turn 222 | 223 | def get_path(self): 224 | '''Calcula el camino a partir del mapa de direcciones''' 225 | 226 | max_steps = self.mapa.max_steps 227 | path = [[4,0]] 228 | wall_count = 0 229 | u_turn_count = 0 230 | for nstep in range(max_steps): 231 | #print('step:', nstep, end=' ') 232 | row, col = path[nstep] 233 | row, col, wall, u_turn = self.step(row, col, self.directions[row, col], path) 234 | wall_count += wall 235 | u_turn_count += u_turn 236 | path.append([row, col]) 237 | if [row,col] == [4, 10]: 238 | break 239 | 240 | self.path, self.wall_count, self.u_turn_count = np.array(path), wall_count, u_turn_count 241 | def deploy_poison(self): 242 | '''Deposita feromonas negativas en las casillas que ha visitado''' 243 | if self.mapa.veneno != 0 : 244 | for i in range(self.path.shape[0]): 245 | row = self.path[i, 0] 246 | col = self.path[i, 1] 247 | if col < 10: 248 | self.poison += self.mapa.feromap[row,col] 249 | self.mapa.feromap[row,col] += 0.1 * self.mapa.veneno 250 | 251 | def draw_path(self, alpha = 0.5, c = 'r', w = 8): 252 | '''Dibuja su camino sobre el mapa. 253 | Es necesario pintar el tablero por separado''' 254 | plt.plot(self.path[:,1], self.path[:,0], c, linewidth = w, alpha = alpha) 255 | 256 | 257 | 258 | def calculate_performances(individual): 259 | '''Calcula las performances de un individuo: 260 | En este caso, el camino a partir del mapa de direcciones. 261 | En este paso también se depositan las feromonas''' 262 | 263 | individual.directions = np.zeros([10,10], dtype=np.int) 264 | for i in range (10): 265 | for j in range (10): 266 | individual.directions[i,j] = individual.traits[(i,j)] 267 | individual.get_path() 268 | individual.deploy_poison() 269 | 270 | def calculate_fitness(individual): 271 | '''Calcula la aptitud de un individuo''' 272 | path, wall_count, u_turn_count = individual.path, individual.wall_count, individual.u_turn_count 273 | poison = individual.poison 274 | max_steps = individual.mapa.max_steps 275 | endx = path[-1,1] 276 | victory = max_steps + 1 - len(path) # >0 si ha llegado al final, mayor cuanto más corto sea el camino 277 | individual.fitness = endx * 4 - 2 * wall_count - 3 * u_turn_count - 0.03 * poison + victory * 5 278 | 279 | def avanzar(mapa, n = 100, 280 | max_pop = 100, min_pop = 10, 281 | reproduction_rate = 8, mutation_rate = 0.05): 282 | '''Efectua una cantidad n de generaciones ''' 283 | for i in range(n): 284 | print(i+1, end='·') 285 | ag.immigration(mapa.list_caminos, max_pop, 286 | calculate_performances, calculate_fitness, 287 | mapa.dict_genes, Camino, mapa) 288 | ag.tournament(mapa.list_caminos, min_pop) 289 | mapa.reload_poison() 290 | ag.crossover(mapa.list_caminos, reproduction_rate, mutation_rate, 291 | calculate_performances, calculate_fitness, 292 | mapa.dict_genes, Camino, mapa) 293 | mapa.statistics() 294 | 295 | def draw_all(mapa): 296 | '''Dibuja el mapa con todas las soluciones de la generación actual, 297 | las feromonas del mapa, y gráficas de evolución.''' 298 | mapa.bestpath.draw_directions() 299 | mapa.draw_poison() 300 | for x in mapa.list_caminos: 301 | x.draw_path(0.1) 302 | 303 | mapa.draw_best() 304 | mapa.draw_history() 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | -------------------------------------------------------------------------------- /Ejercicios/Hormiguero/Hormiguero.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "![Aeropython Logo](/files/imagenes/aeropython_logo.png)" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "#Algoritmo de Optimización por Colonia de Hormigas (ACO)" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "Como vimos en la parte de teoría, el problema del viajante es un problema clásico:\n", 22 | "\n", 23 | "Imaginemos una distribución de ciudades en un mapa. Somos un vendedor que quiere visitarlas todas, sólo una vez cada una, gastando el menor combustible posible. \n", 24 | "\n", 25 | "El algoritmo se basa en varias generaciones sucesivas de hormigas que recorren el mapa viajando de ciudad en ciudad, eligiendo su siguiente ciudad de manera aletoria hasta que las han recorrido todas. En cada etapa del viaje, las hormigas eligen moverse de una ciudad a otra teniendo en cuenta las siguientes reglas:\n", 26 | "\n", 27 | "1. Debe visitar cada ciudad exactamente una vez, excepto la inicial en la que estará dos veces (salida y llegada final);\n", 28 | "2. Una ciudad distante tiene menor posibilidad de ser elegida (Visibilidad);\n", 29 | "3. Cuanto más intenso es el rastro de feromonas de una arista entre dos ciudades, mayor es la probabilidad de que esa arista sea elegida;\n", 30 | "4. Después de haber completado su recorrido, la hormiga deposita feromonas en todas las aristas visitadas, mayor cantidad cuanto más pequeña es la distancia total recorrida;\n", 31 | "5. Después de cada generación, algunas feromonas son evaporadas.\n", 32 | "\n" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "metadata": { 39 | "collapsed": false 40 | }, 41 | "outputs": [], 42 | "source": [ 43 | "#Comencemos importando los paquetes necesarios:\n", 44 | "\n", 45 | "%matplotlib inline \n", 46 | "import numpy as np # Usaremos arrays\n", 47 | "import matplotlib.pyplot as plt # Para pintar resultados\n", 48 | "import ants as ants # Aquí están los objetos del algoritmo" 49 | ] 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "metadata": {}, 54 | "source": [ 55 | "Lo primero que vamos a hacer es crear un mapa que contenga nuestras ciudades. Como primer argumento le pasamos el número de ciudades, y como segundo, el tamaño del mapa. Al ser creado, el mapa generará automáticamente las ciudades en posiciones aleatorias." 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": null, 61 | "metadata": { 62 | "collapsed": false 63 | }, 64 | "outputs": [], 65 | "source": [ 66 | "map1 = ants.Mapa(10)" 67 | ] 68 | }, 69 | { 70 | "cell_type": "markdown", 71 | "metadata": {}, 72 | "source": [ 73 | "Podemos ver el mapa con las ciudades unidas por líneas cuyo grosor depende de la distancia entre ellas:" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": null, 79 | "metadata": { 80 | "collapsed": false 81 | }, 82 | "outputs": [], 83 | "source": [ 84 | "map1.draw_distances()" 85 | ] 86 | }, 87 | { 88 | "cell_type": "markdown", 89 | "metadata": {}, 90 | "source": [ 91 | "A continuación, lo siguiente que tenemos que hacer es crear un enjambre de hormigas. Con esta función, lo podremos hacer fácilmente." 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": null, 97 | "metadata": { 98 | "collapsed": false 99 | }, 100 | "outputs": [], 101 | "source": [ 102 | "map1.swarm_create(100) # Creamos un enjambre de 100 hormigas" 103 | ] 104 | }, 105 | { 106 | "cell_type": "markdown", 107 | "metadata": {}, 108 | "source": [ 109 | "Si queremos ver dónde se encuentran nuestras hormigas en un momento dado, podemos hacerlo:" 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": null, 115 | "metadata": { 116 | "collapsed": false 117 | }, 118 | "outputs": [], 119 | "source": [ 120 | "map1.swarm_show()" 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "metadata": {}, 126 | "source": [ 127 | "Podemos comprobar fácilmente que la matriz de distancias es simétrica, y que la matriz de feromonas aún está vacía:" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": null, 133 | "metadata": { 134 | "collapsed": false 135 | }, 136 | "outputs": [], 137 | "source": [ 138 | "map1.show_distances_matrix()\n", 139 | "map1.show_feromones_matrix()" 140 | ] 141 | }, 142 | { 143 | "cell_type": "markdown", 144 | "metadata": {}, 145 | "source": [ 146 | "Empecemos a mover a nuestras hormigas!\n", 147 | "\n", 148 | "Para que la primera generación de hormigas recorra el mapa llamaremos a la función swarm_generation():" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": null, 154 | "metadata": { 155 | "collapsed": true 156 | }, 157 | "outputs": [], 158 | "source": [ 159 | "map1.swarm_generation()" 160 | ] 161 | }, 162 | { 163 | "cell_type": "markdown", 164 | "metadata": {}, 165 | "source": [ 166 | "Veamos cómo ha cambiado las feromonas!" 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": null, 172 | "metadata": { 173 | "collapsed": false 174 | }, 175 | "outputs": [], 176 | "source": [ 177 | "map1.show_feromones_matrix()\n", 178 | "map1.draw_feromones()" 179 | ] 180 | }, 181 | { 182 | "cell_type": "markdown", 183 | "metadata": {}, 184 | "source": [ 185 | "Para poder encontrar una buena ruta, necesitaremos que pasen unas cuantras generaciones más... Pongamos que 50" 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": null, 191 | "metadata": { 192 | "collapsed": false 193 | }, 194 | "outputs": [], 195 | "source": [ 196 | "for i in range(50):\n", 197 | " print(i, end = '·')\n", 198 | " map1.swarm_generation()\n", 199 | "map1.show_feromones_matrix()\n", 200 | "map1.draw_feromones()" 201 | ] 202 | }, 203 | { 204 | "cell_type": "markdown", 205 | "metadata": {}, 206 | "source": [ 207 | "Parece que las hormigas ya tienen claros sus caminos favoritos!\n", 208 | "\n", 209 | "Veamos qué pinta tiene el mejor camino que han encontrado:" 210 | ] 211 | }, 212 | { 213 | "cell_type": "code", 214 | "execution_count": null, 215 | "metadata": { 216 | "collapsed": false 217 | }, 218 | "outputs": [], 219 | "source": [ 220 | "map1.draw_best_path()" 221 | ] 222 | }, 223 | { 224 | "cell_type": "markdown", 225 | "metadata": {}, 226 | "source": [ 227 | "Podemos borrar las feromonas y empezar de nuevo el algoritmo, para ver si siempre llegan a la misma solución. No te preocupes, al algoritmo no borrará la mejor ruta encontrada." 228 | ] 229 | }, 230 | { 231 | "cell_type": "code", 232 | "execution_count": null, 233 | "metadata": { 234 | "collapsed": false, 235 | "scrolled": false 236 | }, 237 | "outputs": [], 238 | "source": [ 239 | "for j in range(3):\n", 240 | " map1.feromone_reset()\n", 241 | " print()\n", 242 | " print('Ejecución', j+1, ', generación: ')\n", 243 | " for i in range(50):\n", 244 | " print(i+1, end = '·')\n", 245 | " map1.swarm_generation()\n", 246 | " map1.draw_feromones()" 247 | ] 248 | }, 249 | { 250 | "cell_type": "markdown", 251 | "metadata": {}, 252 | "source": [ 253 | "El algoritmo podría haber encontrado una ruta mejor, comprobémoslo:" 254 | ] 255 | }, 256 | { 257 | "cell_type": "code", 258 | "execution_count": null, 259 | "metadata": { 260 | "collapsed": false 261 | }, 262 | "outputs": [], 263 | "source": [ 264 | "map1.draw_best_path()" 265 | ] 266 | }, 267 | { 268 | "cell_type": "markdown", 269 | "metadata": {}, 270 | "source": [ 271 | "Podemos observar cómo han ido variando las longitudes máxima, mínima y media de los caminos de las hormigas en cada generación, y compararlas con la del mejor camino encontrado:" 272 | ] 273 | }, 274 | { 275 | "cell_type": "code", 276 | "execution_count": null, 277 | "metadata": { 278 | "collapsed": false, 279 | "scrolled": true 280 | }, 281 | "outputs": [], 282 | "source": [ 283 | "map1.draw_results()" 284 | ] 285 | }, 286 | { 287 | "cell_type": "markdown", 288 | "metadata": {}, 289 | "source": [ 290 | "También podemos dibujar las longitudes mínimas de cada ejecución del algoritmo:" 291 | ] 292 | }, 293 | { 294 | "cell_type": "code", 295 | "execution_count": null, 296 | "metadata": { 297 | "collapsed": false 298 | }, 299 | "outputs": [], 300 | "source": [ 301 | "map1.draw_best_results()" 302 | ] 303 | }, 304 | { 305 | "cell_type": "markdown", 306 | "metadata": {}, 307 | "source": [ 308 | "##Ajuste fino de las Feromonas" 309 | ] 310 | }, 311 | { 312 | "cell_type": "markdown", 313 | "metadata": {}, 314 | "source": [ 315 | "Supongamos ahora que queremos optimizar una ruta entre 40 ciudades:" 316 | ] 317 | }, 318 | { 319 | "cell_type": "code", 320 | "execution_count": null, 321 | "metadata": { 322 | "collapsed": false 323 | }, 324 | "outputs": [], 325 | "source": [ 326 | "map2 = ants.Mapa(40)\n", 327 | "map2.swarm_create(200)\n", 328 | "map2.swarm_generation()\n", 329 | "map2.show_feromones_matrix()\n", 330 | "map2.draw_feromones()" 331 | ] 332 | }, 333 | { 334 | "cell_type": "markdown", 335 | "metadata": {}, 336 | "source": [ 337 | "¿Qué pasa? ¡No hay feromonas!\n", 338 | "\n", 339 | "La solución es muy simple: las hormigas \"standard\" están adaptadas a mapas más pequeños, y no dejan tras de sí suficiente feromona como para que no se evapore toda. \n", 340 | "\n", 341 | "Para solucionarlo, podemos modificar a nuestras hormigas para este entorno:" 342 | ] 343 | }, 344 | { 345 | "cell_type": "code", 346 | "execution_count": null, 347 | "metadata": { 348 | "collapsed": false 349 | }, 350 | "outputs": [], 351 | "source": [ 352 | "#Con un valor de 5 es suficiente\n", 353 | "map2.feromone_fine_tune()" 354 | ] 355 | }, 356 | { 357 | "cell_type": "code", 358 | "execution_count": null, 359 | "metadata": { 360 | "collapsed": false 361 | }, 362 | "outputs": [], 363 | "source": [ 364 | "map2.swarm_generation()\n", 365 | "map2.show_feromones_matrix()\n", 366 | "map2.draw_feromones()" 367 | ] 368 | }, 369 | { 370 | "cell_type": "code", 371 | "execution_count": null, 372 | "metadata": { 373 | "collapsed": false 374 | }, 375 | "outputs": [], 376 | "source": [ 377 | "for i in range(25):\n", 378 | " print(i, end = '·')\n", 379 | " map2.swarm_generation()\n", 380 | "map2.show_feromones_matrix()\n", 381 | "map2.draw_feromones()\n", 382 | "map2.draw_best_path()\n", 383 | "map2.draw_results()" 384 | ] 385 | }, 386 | { 387 | "cell_type": "markdown", 388 | "metadata": { 389 | "collapsed": true 390 | }, 391 | "source": [ 392 | "## Te toca trabajar" 393 | ] 394 | }, 395 | { 396 | "cell_type": "markdown", 397 | "metadata": { 398 | "collapsed": true 399 | }, 400 | "source": [ 401 | "Ahora que ya hemos visto cómo funciona este algoritmo, probemos a cambiarlo. ¿Cómo funcionaría si no todas las ciudades estuvieran conectadas entre sí? Intenta cambiar el algoritmo para modelar este caso. Probablemente la manera más fácil sea modificar la matriz de distancias, hinchando el valor de algunas distancias por encima de su valor real para penalizar ciertas rutas. ¿Qué pasaría si la matriz se quedara asimétrica?\n", 402 | "\n", 403 | "Una vez que lo hayas conseguido, te propongo el problema de enrutación de paquetes: En vez de recorrer todo el mapa, intenta ir de un nodo dado a otro. Puedes añadir feromonas atractivas en ciertos nodos para representar que son nodos de la red que están sobredimensionados, o crear feromonas negativas y esparcirlos por otros nodos para representar enlaces que están sobrecargados y es mejor evitar. Recuerda que las feromonas se disipan un poco cada generación, para efectos duraderos deberas añadirlas también cada turno." 404 | ] 405 | }, 406 | { 407 | "cell_type": "code", 408 | "execution_count": null, 409 | "metadata": { 410 | "collapsed": true 411 | }, 412 | "outputs": [], 413 | "source": [] 414 | }, 415 | { 416 | "cell_type": "code", 417 | "execution_count": null, 418 | "metadata": { 419 | "collapsed": true 420 | }, 421 | "outputs": [], 422 | "source": [] 423 | }, 424 | { 425 | "cell_type": "code", 426 | "execution_count": null, 427 | "metadata": { 428 | "collapsed": true 429 | }, 430 | "outputs": [], 431 | "source": [] 432 | }, 433 | { 434 | "cell_type": "markdown", 435 | "metadata": {}, 436 | "source": [ 437 | "Siro Moreno, Aeropython, 8 de Noviembre de 2015\n" 438 | ] 439 | }, 440 | { 441 | "cell_type": "code", 442 | "execution_count": null, 443 | "metadata": { 444 | "collapsed": true 445 | }, 446 | "outputs": [], 447 | "source": [] 448 | } 449 | ], 450 | "metadata": { 451 | "kernelspec": { 452 | "display_name": "Python 3", 453 | "language": "python", 454 | "name": "python3" 455 | }, 456 | "language_info": { 457 | "codemirror_mode": { 458 | "name": "ipython", 459 | "version": 3 460 | }, 461 | "file_extension": ".py", 462 | "mimetype": "text/x-python", 463 | "name": "python", 464 | "nbconvert_exporter": "python", 465 | "pygments_lexer": "ipython3", 466 | "version": "3.4.3" 467 | } 468 | }, 469 | "nbformat": 4, 470 | "nbformat_minor": 0 471 | } 472 | -------------------------------------------------------------------------------- /Ejercicios/El vecindario racista/El vecindario racista.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "![Aeropython Logo](/files/imagenes/aeropython_logo.png)" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "#El vecindario racista: el modelo de segregación de Schelling " 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": { 20 | "collapsed": true 21 | }, 22 | "source": [ 23 | "La segregación racial es un problema en muchas partes del mundo desde hace mucho tiempo. A pesar de que ciertos colectivos han realizado un gran esfuerzo por solucionarlo, muchos países continúan segregados por razones étnicas, de credo, sexo, riqueza, etc. ¿Por qué es un problema tan complicado de resolver?\n", 24 | "\n", 25 | "En 1971, el economista americano Thomas Schelling creó un modelo basado en agentes que podría ayudar a explicar por qué la segregación es un problema tan complicado de combatir. Su modelo de segregación mostraba que individuos o \"agentes\" que no eran especialmente rigurosos respecto a su entorno tendían aún así a segregarse con el tiempo. A pesar de que el modelo es especialmente simple, permite una interesante perspectiva sobre cómo los individuos pueden tender a segregarse, a pesar de no tener un especial deseo por hacerlo.\n", 26 | "\n", 27 | "(Traducción de http://nifty.stanford.edu/2014/mccown-schelling-model-segregation/ )\n", 28 | "\n", 29 | "(Enlace al paper original de Schelling para Harvard: http://www.stat.berkeley.edu/~aldous/157/Papers/Schelling_Seg_Models.pdf )" 30 | ] 31 | }, 32 | { 33 | "cell_type": "markdown", 34 | "metadata": {}, 35 | "source": [ 36 | "##Planteamiento" 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": {}, 42 | "source": [ 43 | "Para este ejercicio usaremos numpy, matplotlib y el código que está en la carpeta vecindario." 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": 1, 49 | "metadata": { 50 | "collapsed": true 51 | }, 52 | "outputs": [], 53 | "source": [ 54 | "%matplotlib inline\n", 55 | "import numpy as np\n", 56 | "import matplotlib.pyplot as plt\n", 57 | "import vecindario as vc" 58 | ] 59 | }, 60 | { 61 | "cell_type": "markdown", 62 | "metadata": { 63 | "collapsed": true 64 | }, 65 | "source": [ 66 | "Supongamos que tenemos un vecindario. Este vecindario es una matriz o casillero, en el que cada vecino puede ocupar una casilla." 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": null, 72 | "metadata": { 73 | "collapsed": false 74 | }, 75 | "outputs": [], 76 | "source": [ 77 | "mundo, colores = vc.crear_mundo()\n", 78 | "vc.vecin_print(mundo, colores, 10, 0)" 79 | ] 80 | }, 81 | { 82 | "cell_type": "markdown", 83 | "metadata": {}, 84 | "source": [ 85 | "Aquí podemos ver un pequeño vecindario, con vecinos azules y rojos. También hay espacios libres que no están ocupados por nadie.\n", 86 | "\n", 87 | "Cada vecino se ve afectado por las 8 casillas que tiene a su alrededor. En principio, no le molesta la presencia de vecinos de un color diferente, pero si la proporción de vecinos de su mismo color es de sólo 1/3 o menos, se sentirá incomodado y deseará marcharse.\n", 88 | "\n", 89 | "En el gráfico superior, los vecinos incomodados tienen unas esquinas grises, mientras que los vecinos confortables no.\n", 90 | "\n", 91 | "Estos vecinos que se sienten incómodos se mudarán en cuanto puedan. Para representar esto, repasaremos la lista de vecinos, detectando a los que quieren mudarse, y cambiarán de sitio a una casilla nueva aleatoria que esté vacía.\n", 92 | "\n", 93 | "Esto representa un 'step'." 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": null, 99 | "metadata": { 100 | "collapsed": false 101 | }, 102 | "outputs": [], 103 | "source": [ 104 | "n = vc.step_mudanza(mundo, colores, 1, 10)" 105 | ] 106 | }, 107 | { 108 | "cell_type": "markdown", 109 | "metadata": {}, 110 | "source": [ 111 | "Podemos comprobar que aunque los individuos no prefieren ningún tipo de segregación, y su única condición es que al menos 1/3 de sus vecinos sean del mismo color que ellos, al cabo de unos pocos steps la segregación en grupos homogéneos ha aparecido como propiedad emergente." 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": null, 117 | "metadata": { 118 | "collapsed": false 119 | }, 120 | "outputs": [], 121 | "source": [ 122 | "n = vc.step_multiple(mundo, colores, n, 10)" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": {}, 128 | "source": [ 129 | "Cómo podemos evitar esta situación?\n", 130 | "\n", 131 | "Existe una sencilla manera de evitarlo: que los individuos activamente trabajen para evitar la segregación. La manera de implementar esto es muy sencilla: basta con que también se sientan incómodos si más del 90% de sus vecinos son iguales a ellos para obtener cambios sustanciales en la segregación del grupo." 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": null, 137 | "metadata": { 138 | "collapsed": false 139 | }, 140 | "outputs": [], 141 | "source": [ 142 | "mundo, colores = vc.crear_mundo(intom = 90)\n", 143 | "n = vc.step_multiple(mundo, colores, 0, 10, numsteps=100)" 144 | ] 145 | }, 146 | { 147 | "cell_type": "markdown", 148 | "metadata": {}, 149 | "source": [ 150 | "##Extendiendo el algoritmo" 151 | ] 152 | }, 153 | { 154 | "cell_type": "markdown", 155 | "metadata": { 156 | "collapsed": true 157 | }, 158 | "source": [ 159 | "Ahora que ya hemos visto cómo trabaja este modelo, vamos a jugar un poco con sus parámetros. ¿Te atreves a predecir cómo influirán estos números en un vecindario de tres colores?" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": null, 165 | "metadata": { 166 | "collapsed": true 167 | }, 168 | "outputs": [], 169 | "source": [ 170 | "dim = 50 #Tamaño del lado de la matriz\n", 171 | "vacios = 10 #Porcentaje de huecos\n", 172 | "colors = 3 #Número de colores\n", 173 | "prop = [0.33, 0.33]\n", 174 | "n = 0\n", 175 | "intolerance = 33 #Porcentaje mínimo de vecinos iguales\n", 176 | "intom = 100 #Porcentaje máximo de vecinos iguales\n", 177 | "rad = 1 # Radio en el que el color de los vecinos se comprueba \n", 178 | "historia_felicidad = [] # Aquí vamos a almacenar la felicidad del grupo en cada step\n", 179 | "historia_segregacion = []# Y aquí, el valor de la segregación." 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": null, 185 | "metadata": { 186 | "collapsed": false 187 | }, 188 | "outputs": [], 189 | "source": [ 190 | "#Veamos cómo comienza la simulación, con los vecinos repartidos aleatoriamente.\n", 191 | "par, datacolor = vc.crear_mundo(dim, colors= colors, prop= prop, vacios = vacios,\n", 192 | " intolerance=intolerance, intom= intom, rad = rad,\n", 193 | " h_fel = historia_felicidad, h_seg = historia_segregacion)\n", 194 | "\n", 195 | "vc.vecin_print(par, datacolor, dim, n)" 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": null, 201 | "metadata": { 202 | "collapsed": false 203 | }, 204 | "outputs": [], 205 | "source": [ 206 | "#Qué pasará dentro de 50 steps?\n", 207 | "n = vc.step_multiple(par, datacolor, n, dim,\n", 208 | " historia_felicidad, historia_segregacion, 50)" 209 | ] 210 | }, 211 | { 212 | "cell_type": "markdown", 213 | "metadata": {}, 214 | "source": [ 215 | "¡Sorpresa! ¿Esperabas esto?\n", 216 | "\n", 217 | "También podemos observar cómo han ido evolucionando:" 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "execution_count": null, 223 | "metadata": { 224 | "collapsed": false 225 | }, 226 | "outputs": [], 227 | "source": [ 228 | "vc.evolucion(historia_felicidad, historia_segregacion)" 229 | ] 230 | }, 231 | { 232 | "cell_type": "markdown", 233 | "metadata": {}, 234 | "source": [ 235 | "¿Qué crees que pasará en un vecindario igual pero más concienciado?" 236 | ] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": null, 241 | "metadata": { 242 | "collapsed": true 243 | }, 244 | "outputs": [], 245 | "source": [ 246 | "intom = 90 #Porcentaje máximo de vecinos iguales\n", 247 | "n = 0 #Vamos a comenzar de 0\n", 248 | "historia_felicidad = [] # Aquí vamos a almacenar la felicidad del grupo en cada step\n", 249 | "historia_segregacion = []# Y aquí, el valor de la segregación." 250 | ] 251 | }, 252 | { 253 | "cell_type": "code", 254 | "execution_count": null, 255 | "metadata": { 256 | "collapsed": false 257 | }, 258 | "outputs": [], 259 | "source": [ 260 | "#Veamos cómo comienza la simulación, con los vecinos repartidos aleatoriamente.\n", 261 | "par, datacolor = vc.crear_mundo(dim, colors= colors, prop= prop, vacios = vacios,\n", 262 | " intolerance=intolerance, intom= intom, rad = rad,\n", 263 | " h_fel = historia_felicidad, h_seg = historia_segregacion)\n", 264 | "\n", 265 | "vc.vecin_print(par, datacolor, dim, n)" 266 | ] 267 | }, 268 | { 269 | "cell_type": "code", 270 | "execution_count": null, 271 | "metadata": { 272 | "collapsed": false 273 | }, 274 | "outputs": [], 275 | "source": [ 276 | "#Qué pasará dentro de 50 steps?\n", 277 | "n = vc.step_multiple(par, datacolor, n, dim,\n", 278 | " historia_felicidad, historia_segregacion, 50)" 279 | ] 280 | }, 281 | { 282 | "cell_type": "code", 283 | "execution_count": null, 284 | "metadata": { 285 | "collapsed": false 286 | }, 287 | "outputs": [], 288 | "source": [ 289 | "vc.evolucion(historia_felicidad, historia_segregacion)" 290 | ] 291 | }, 292 | { 293 | "cell_type": "markdown", 294 | "metadata": {}, 295 | "source": [ 296 | "Al ser un requisito más restrictivo ahora, comprobamos que el porcentaje de vecinos satisfechos aumenta más lentamente, pero sin embargo, el impacto en la segregación es muy notable.\n", 297 | "\n", 298 | "Ahora queda a tu criterio experimentar con los valores. ¡Investiga qué patrones forman las diferentes combinaciones de parámetros!" 299 | ] 300 | }, 301 | { 302 | "cell_type": "markdown", 303 | "metadata": {}, 304 | "source": [ 305 | "##Más sobre patrones emergentes: los Patrones de Turing" 306 | ] 307 | }, 308 | { 309 | "cell_type": "markdown", 310 | "metadata": {}, 311 | "source": [ 312 | "![Patrones de Turing](/files/imagenes/turingpattern.jpg)" 313 | ] 314 | }, 315 | { 316 | "cell_type": "markdown", 317 | "metadata": { 318 | "collapsed": true 319 | }, 320 | "source": [ 321 | "Si te ha gustado cómo se pueden formar patrones como propiedad emergente de un sistema complejo, probablemente te interese investigar sobre los Patrones de Turing. Este tema también está relacionado con el anterior a traves de la importancia de la tolerancia y el respeto en la sociedad, ya que fue el último trabajo que Turing publicó antes de \"suicidarse\" tras ser condenado a castración química por su homosexualidad.\n", 322 | "\n", 323 | "En sus últimos años de vida, el gran matemático Alan Turing planteó la cuestión de la teoría de la morfogénesis, es decir, cómo un ser vivo cuyas células tienen todas el mismo código genético es capaz de desarrollar una forma compleja, como extremidades, dedos, etc.\n", 324 | "\n", 325 | "En el caso concreto de los patrones del color de la piel, estudió la posibilidad de que diferentes concentraciones de elementos químicos que reaccionan entre sí según fórmulas sencillas pudieran producirlos, y los resultados fueron sorprendentes.\n", 326 | "\n", 327 | "Puedes encontrar una introducción al tema en estos artículos cortos:\n", 328 | "\n", 329 | "http://francis.naukas.com/2010/09/24/alan-turing-el-genio-matematico-que-creo-la-teoria-de-la-morfogenesis-poco-antes-de-suicidarse/\n", 330 | "\n", 331 | "http://nadaesgratis.es/anxo-sanchez/turing-y-sus-patrones-el-pionero-de-la-biologia-matematica\n", 332 | "\n", 333 | "Imagen extraída de: http://francis.naukas.com/2009/08/08/generacion-de-patrones-espaciotemporales-en-nuevos-tipos-de-reacciones-quimicas/" 334 | ] 335 | }, 336 | { 337 | "cell_type": "markdown", 338 | "metadata": { 339 | "collapsed": true 340 | }, 341 | "source": [ 342 | "##Te toca trabajar" 343 | ] 344 | }, 345 | { 346 | "cell_type": "markdown", 347 | "metadata": { 348 | "collapsed": true 349 | }, 350 | "source": [ 351 | "Hemos visto cómo funciona la formación de patrones cuando todos los individuos tienen las mismas características, pero... ¿Cómo se formarían si no las tuvieran?\n", 352 | "\n", 353 | "Ve al código de la carpeta 'vecindario' y modifícalo de manera que cuando se crea la población, los individuos reciban una diferencia aletoria de magnitud controlada en sus niveles de intolerancia. ¡Observa cómo afecta este parámetro en la población!\n", 354 | "\n", 355 | "Estas funciones pueden serte de ayuda:\n", 356 | "\n", 357 | " np.random.rand() \n", 358 | " np.random.randn()" 359 | ] 360 | }, 361 | { 362 | "cell_type": "code", 363 | "execution_count": null, 364 | "metadata": { 365 | "collapsed": true 366 | }, 367 | "outputs": [], 368 | "source": [] 369 | }, 370 | { 371 | "cell_type": "markdown", 372 | "metadata": {}, 373 | "source": [ 374 | "Siro Moreno, Aeropython, 28 de Octubre de 2015" 375 | ] 376 | }, 377 | { 378 | "cell_type": "markdown", 379 | "metadata": { 380 | "collapsed": true 381 | }, 382 | "source": [ 383 | "Basado en el ejercicio 'Segregation' del paquete Evolife del profesor Jean Louis Dessalles" 384 | ] 385 | }, 386 | { 387 | "cell_type": "code", 388 | "execution_count": null, 389 | "metadata": { 390 | "collapsed": true 391 | }, 392 | "outputs": [], 393 | "source": [] 394 | } 395 | ], 396 | "metadata": { 397 | "kernelspec": { 398 | "display_name": "Python 3", 399 | "language": "python", 400 | "name": "python3" 401 | }, 402 | "language_info": { 403 | "codemirror_mode": { 404 | "name": "ipython", 405 | "version": 3 406 | }, 407 | "file_extension": ".py", 408 | "mimetype": "text/x-python", 409 | "name": "python", 410 | "nbconvert_exporter": "python", 411 | "pygments_lexer": "ipython3", 412 | "version": "3.5.2" 413 | } 414 | }, 415 | "nbformat": 4, 416 | "nbformat_minor": 0 417 | } 418 | -------------------------------------------------------------------------------- /Bloque B1 - Metaheurística-Vacío.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "![Aropython_logo](./static/aeropython_name_mini.png)\n", 8 | "###### Carlos Dorado Cárdenas\n", 9 | "###### Siro Moreno Martín" 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "# Simplifica tu vida con sistemas complejos y algoritmos genéticos" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "## Bloque B1: Metaheurística" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 1, 29 | "metadata": { 30 | "collapsed": false 31 | }, 32 | "outputs": [], 33 | "source": [ 34 | "%matplotlib inline\n", 35 | "import matplotlib.pyplot as plt\n", 36 | "import numpy as np\n", 37 | "import random as random" 38 | ] 39 | }, 40 | { 41 | "cell_type": "markdown", 42 | "metadata": {}, 43 | "source": [ 44 | "##Ejercicio 1: \n", 45 | "\n", 46 | "Sin escribir código, haya el valor de _x_ que minimiza el valor de la función _caja_negra_ en el intervalo x ∈ [-20, 20]." 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": 2, 52 | "metadata": { 53 | "collapsed": false 54 | }, 55 | "outputs": [], 56 | "source": [ 57 | "from codigo import caja_negra" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 3, 63 | "metadata": { 64 | "collapsed": false, 65 | "scrolled": true 66 | }, 67 | "outputs": [ 68 | { 69 | "data": { 70 | "text/plain": [ 71 | "5.4723051280883039" 72 | ] 73 | }, 74 | "execution_count": 3, 75 | "metadata": {}, 76 | "output_type": "execute_result" 77 | } 78 | ], 79 | "source": [ 80 | "caja_negra([20])" 81 | ] 82 | }, 83 | { 84 | "cell_type": "markdown", 85 | "metadata": {}, 86 | "source": [ 87 | "##Ejercicio 2: \n", 88 | "\n", 89 | "Usando un algoritmo de búsqueda aleatoria, minimiza la función de Bohachevski en el cuadrado x1, x2 ∈ [-100, 100]." 90 | ] 91 | }, 92 | { 93 | "cell_type": "markdown", 94 | "metadata": {}, 95 | "source": [ 96 | "\"Función" 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": 4, 102 | "metadata": { 103 | "collapsed": false 104 | }, 105 | "outputs": [], 106 | "source": [ 107 | "from codigo import bohachevski" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": 5, 113 | "metadata": { 114 | "collapsed": false 115 | }, 116 | "outputs": [], 117 | "source": [ 118 | "def produce_random_solution (dimensions, minimum, maximum):\n", 119 | " \n", 120 | " random_solution =[]\n", 121 | " \n", 122 | " while dimensions > 0:\n", 123 | "\n", 124 | " random_solution.append(minimum + (maximum - minimum) * random.random())\n", 125 | " \n", 126 | " dimensions -= 1\n", 127 | " \n", 128 | " return random_solution" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": null, 134 | "metadata": { 135 | "collapsed": true 136 | }, 137 | "outputs": [], 138 | "source": [ 139 | "def random_search (number_of_tries, function, dimensions, minimum, maximum):\n", 140 | " \n", 141 | " return best" 142 | ] 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "metadata": {}, 147 | "source": [ 148 | "##Ejercicio 3: \n", 149 | "\n", 150 | "Usando un algoritmo de ascenso de colinas, minimiza la función de Bohachevski en el cuadrado x1, x2 ∈ [-100, 100]." 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": 6, 156 | "metadata": { 157 | "collapsed": true 158 | }, 159 | "outputs": [], 160 | "source": [ 161 | "def produce_tweaked_solution (solution, tweak_range, minimum, maximum):\n", 162 | " \n", 163 | " tweaked_solution = []\n", 164 | " \n", 165 | " for element in solution:\n", 166 | " \n", 167 | " while True:\n", 168 | " \n", 169 | " change = tweak_range * (random.random()-0.5)\n", 170 | " \n", 171 | " if minimum <= element + change <= maximum:\n", 172 | " tweaked_solution.append(element + change)\n", 173 | " break\n", 174 | " \n", 175 | " return tweaked_solution\n", 176 | " " 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": 14, 182 | "metadata": { 183 | "collapsed": true 184 | }, 185 | "outputs": [], 186 | "source": [ 187 | "def hill_climbing (number_of_tries, function, dimensions, minimum, maximum):\n", 188 | "\n", 189 | " return best" 190 | ] 191 | }, 192 | { 193 | "cell_type": "markdown", 194 | "metadata": {}, 195 | "source": [ 196 | "##Ejercicio 4: \n", 197 | "\n", 198 | "Usando ambos tipos de algoritmo, minimiza la función de Easom en el cuadrado x1, x2 ∈ [-100, 100]." 199 | ] 200 | }, 201 | { 202 | "cell_type": "markdown", 203 | "metadata": {}, 204 | "source": [ 205 | "\"Función" 206 | ] 207 | }, 208 | { 209 | "cell_type": "code", 210 | "execution_count": 17, 211 | "metadata": { 212 | "collapsed": false 213 | }, 214 | "outputs": [], 215 | "source": [ 216 | "from codigo import easom" 217 | ] 218 | }, 219 | { 220 | "cell_type": "markdown", 221 | "metadata": {}, 222 | "source": [ 223 | "##Ejercicio 5: \n", 224 | "\n", 225 | "Implementa un algoritmo de ascenso de colinas con reinicios aleatorios. Pruébalo con las funciones de Bohachevski e Easom. Minimiza la función de Goldstein-Price en el cuadrado x1, x2 ∈ [-2, 2]." 226 | ] 227 | }, 228 | { 229 | "cell_type": "markdown", 230 | "metadata": {}, 231 | "source": [ 232 | "\"Función" 233 | ] 234 | }, 235 | { 236 | "cell_type": "code", 237 | "execution_count": 21, 238 | "metadata": { 239 | "collapsed": false 240 | }, 241 | "outputs": [], 242 | "source": [ 243 | "from codigo import goldstein_price" 244 | ] 245 | }, 246 | { 247 | "cell_type": "code", 248 | "execution_count": 22, 249 | "metadata": { 250 | "collapsed": false 251 | }, 252 | "outputs": [], 253 | "source": [ 254 | "def hcrs (number_of_restarts, climbing_tries, function, dimensions, minimum, maximum):\n", 255 | " \n", 256 | " return best" 257 | ] 258 | }, 259 | { 260 | "cell_type": "code", 261 | "execution_count": 26, 262 | "metadata": { 263 | "collapsed": false, 264 | "scrolled": true 265 | }, 266 | "outputs": [ 267 | { 268 | "data": { 269 | "text/html": [ 270 | "" 271 | ], 272 | "text/plain": [ 273 | "" 274 | ] 275 | }, 276 | "execution_count": 26, 277 | "metadata": {}, 278 | "output_type": "execute_result" 279 | } 280 | ], 281 | "source": [ 282 | "from IPython.display import HTML\n", 283 | "\n", 284 | "HTML('')" 285 | ] 286 | }, 287 | { 288 | "cell_type": "markdown", 289 | "metadata": {}, 290 | "source": [ 291 | "Si te está gustando este taller:" 292 | ] 293 | }, 294 | { 295 | "cell_type": "code", 296 | "execution_count": 1, 297 | "metadata": { 298 | "collapsed": false 299 | }, 300 | "outputs": [ 301 | { 302 | "data": { 303 | "text/html": [ 304 | "Tweet\n", 305 | "" 306 | ], 307 | "text/plain": [ 308 | "" 309 | ] 310 | }, 311 | "metadata": {}, 312 | "output_type": "display_data" 313 | } 314 | ], 315 | "source": [ 316 | "%%html\n", 317 | "Tweet\n", 318 | "" 319 | ] 320 | }, 321 | { 322 | "cell_type": "markdown", 323 | "metadata": {}, 324 | "source": [ 325 | "Carlos Dorado, Aeropython, 7 de Octubre de 2016" 326 | ] 327 | }, 328 | { 329 | "cell_type": "code", 330 | "execution_count": 2, 331 | "metadata": { 332 | "collapsed": false 333 | }, 334 | "outputs": [ 335 | { 336 | "data": { 337 | "text/html": [ 338 | "\n", 339 | "\n", 340 | "\n", 341 | "\n", 464 | "\n" 480 | ], 481 | "text/plain": [ 482 | "" 483 | ] 484 | }, 485 | "execution_count": 2, 486 | "metadata": {}, 487 | "output_type": "execute_result" 488 | } 489 | ], 490 | "source": [ 491 | "# Notebook style\n", 492 | "from IPython.core.display import HTML\n", 493 | "css_file = './static/style.css'\n", 494 | "HTML(open(css_file, \"r\").read())" 495 | ] 496 | } 497 | ], 498 | "metadata": { 499 | "kernelspec": { 500 | "display_name": "Python 3", 501 | "language": "python", 502 | "name": "python3" 503 | }, 504 | "language_info": { 505 | "codemirror_mode": { 506 | "name": "ipython", 507 | "version": 3 508 | }, 509 | "file_extension": ".py", 510 | "mimetype": "text/x-python", 511 | "name": "python", 512 | "nbconvert_exporter": "python", 513 | "pygments_lexer": "ipython3", 514 | "version": "3.4.1" 515 | } 516 | }, 517 | "nbformat": 4, 518 | "nbformat_minor": 0 519 | } 520 | -------------------------------------------------------------------------------- /Ejercicios/El vecindario racista/vecindario/_vecindario.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """Ejercicio del Vecindario Racista 5 | 6 | Taller de la PyConEs 2015: Simplifica tu vida con sistemas complejos y algoritmos genéticos 7 | 8 | Este script contiene las funciones y clases necesarias para ejecutar una simulación del 9 | modelo de emergencia de segregación propuesto por Schelling. 10 | 11 | http://www.stat.berkeley.edu/~aldous/157/Papers/Schelling_Seg_Models.pdf 12 | 13 | Este script usa arrays de numpy, aunque no debería ser difícil para alguien con experiencia 14 | sustituírlos por otras estructuras si es necesario. 15 | 16 | También usa la librería Matplotlib en las funciones que dibujan resultados.""" 17 | 18 | 19 | import numpy as np 20 | import matplotlib.pyplot as plt 21 | 22 | #Primero vamos a definir los dos tipos de objeto que necesitamos: 23 | 24 | class Parcela: 25 | '''Este objeto representa la finca o mundo en el que viven los vecinos 26 | 27 | Controla todas las variables globales y contiene la lista de vecinos''' 28 | def __init__(self, dim1, intolerance = 55, intomax = 100, dim2 = 0, r = 1): 29 | '''Esta función se ejecuta al crear el objeto: 30 | Asignamos a las variables locales una serie de valores que debemos pasar 31 | como argumento a la hora de crear el mundo: 32 | -Su anchura 33 | -La intolerancia de sus ciudadanos(por defecto, 55) 34 | -Su intolerancia máxima(proporción de vecinos del mismo color máximos para que no quiera mudarse) 35 | -Su altura (por defecto, igual que la anchura) 36 | -El radio de análisis de confort''' 37 | self.dim1 = dim1 38 | if dim2 == 0: #Si la altura es 0 (por defecto), la hacemos igual a la anchura 39 | self.dim2 = dim1 40 | else: 41 | self.dim2 = dim2 42 | self.r = r 43 | self.intolerance = intolerance 44 | self.intomax = intomax 45 | self.tot_pos = self.dim1 * self.dim2 #Número total de casillas 46 | self.libres = list(range(self.tot_pos))#En este momento, todas las casillas están libres 47 | self.matrix = np.zeros((self.dim2, self.dim1), dtype=np.int8)#Esta matriz representa al vecindario. 48 | #Esta matriz empieza llena de ceros (aún no hay nade viviendo), pero conforme vayamos 49 | #añadiendo vecinos, sus casillas se llenarán con el número que representa al color del ocupante. 50 | self.matrix_ampli = np.zeros((self.dim2 + 2 * r, self.dim1 + 2 * self.r), dtype=np.int8) 51 | #Esta matriz es igual que la anterior, pero con un reborde vacío alrededor que luego nos será útil al 52 | #al calcular los vecinos de las casillas que están en los bordes. 53 | self.listavecinos = [] 54 | #En esta lista, que aún está vacía, iremos almacenando a los vecinos que vayamos creando. Así, 55 | #luego los podremos ir llamando fácilmente. 56 | def coord(self, num, dim1): 57 | '''Devuelve las coordenadas del elemento enésimo de la matriz''' 58 | fila = num//dim1 59 | columna = num - fila * dim1 60 | return[fila, columna] 61 | def asignar(self): 62 | '''Escoge una casilla aleatoria libre y devuelve su número. Luego, la borra de 63 | la lista de casillas libres.''' 64 | n = np.random.randint(0, len(self.libres)) 65 | return self.libres.pop(n) 66 | def liberar(self, f, c): 67 | '''Libera una casilla ocupada, borrándola de las matrices y añadiéndola a la lista 68 | de casillas libres''' 69 | n = f * self.dim1 + c #Calcula el número de la casilla a partir de las coordenadas 70 | self.libres.append(n) 71 | self.matrix[f, c] = 0 72 | self.matrix_ampli[f + 1 , c + 1 ] = 0 73 | def nuevo(self, color): 74 | '''Crea un nuevo vecino y le asigna una casilla libre aleatoria''' 75 | n = self.asignar() #Escoge una casilla libre aleatoria 76 | coordenadas = self.coord(n, self.dim1)#Calcula sus coordenadas 77 | self.matrix[coordenadas[0], coordenadas[1]] = color# Asigna a las matrices el color del nuevo vecino 78 | self.matrix_ampli[coordenadas[0] + self.r , coordenadas[1] + self.r ] = color 79 | self.listavecinos.append(Vecino(color, self, coordenadas, 80 | self.intolerance, self.intomax))#Añade el nuevo a la lista de vecinos 81 | def clear(self): 82 | '''Borra a todos los vecinos y deja todo limpio y en blanco''' 83 | self.libres = list(range(self.tot_pos)) 84 | self.matrix = np.zeros((self.dim2, self.dim1), dtype=np.int8) 85 | self.matrix_ampli = np.zeros((self.dim2 + 2*self.r, self.dim1 + 2*self.r), dtype=np.int8) 86 | self.listavecinos = [] 87 | def entorno(self, f, c, color): 88 | '''Dada una casilla y un color, analiza el entorno de la casilla y 89 | devuelve la proporción de casillas ocupadas de ese color''' 90 | #El parámetro r de análisis se pasa de manera implícita dentro de self 91 | r = self.r 92 | vecinos_ocupados = -1 #Empiezan a -1 porque el bucle contará a la propia casilla 93 | vecinos_iguales = -1 94 | for xx in range(1 + 2 * r): 95 | for yy in range(1 + 2 * r): 96 | f_a = f + xx 97 | c_a = c + yy 98 | color_a = self.matrix_ampli.item(f_a, c_a)#Usamos la matriz ampliada para poder analizar fácilmente 99 | # las casillas que están en los bordes sin complicar la notación 100 | if color_a == 0 : 101 | pass 102 | elif color_a == color : 103 | vecinos_ocupados += 1 104 | vecinos_iguales += 1 105 | else: 106 | vecinos_ocupados += 1 107 | if vecinos_ocupados == 0 : 108 | return 1. 109 | else: 110 | return vecinos_iguales / vecinos_ocupados 111 | 112 | class Vecino: 113 | ''' 114 | Este objeto representa a cada vecino del vecindario, es decir, cada circulito. 115 | 116 | Este objeto está pensado para ser llamado sólo desde el objeto "Parcela". 117 | ''' 118 | def __init__(self, color, parcela, coords, intolerance = 55, intomax = 100): 119 | '''Esta función se ejecuta al crear el objeto: 120 | Asignamos a las variables locales una serie de valores que debemos pasar 121 | como argumento a la hora de crear cada ciudadano: 122 | -Su color 123 | -Su intolerancia(proporción de vecinos del mismo color mínimos para que no quiera mudarse) 124 | -Su intolerancia máxima(proporción de vecinos del mismo color máximos para que no quiera mudarse) 125 | -Desde qué parcela se le ha invocado (para luego poder usarla desde él) 126 | -Sus coordenadas''' 127 | self.color = color 128 | self.intolerance = intolerance 129 | self.intomax = intomax 130 | self.parcela = parcela 131 | self.coords = coords 132 | def mudanza(self): 133 | '''Esta función sirve para mudar el vecino desde su posición a otro sitio''' 134 | n = self.parcela.asignar() #Primero se le asigna un sitio libre de la parcela 135 | r = self.parcela.r 136 | coordenadas = self.parcela.coord(n, self.parcela.dim1)# Se calculan las coord del nuevo sitio 137 | self.parcela.matrix[coordenadas[0], coordenadas[1]] = self.color# Se cambia el color del nuevo sitio en la matriz 138 | self.parcela.matrix_ampli[coordenadas[0] + r , coordenadas[1] + r ] = self.color#Y en la matriz ampliada 139 | self.parcela.liberar(self.coords[0], self.coords[1])# Se avisa a la parcela de que el sitio viejo está libre 140 | self.coords = coordenadas# Se cambian las coordenadas que el vecino tiene guardadas a las nuevas 141 | def satisfecho(self): 142 | '''Esta función sirve para saver si el vecino está satisfecho con su entorno''' 143 | vecin_prop = self.parcela.entorno(self.coords[0], self.coords[1], self.color)#Primero, en 144 | #la parcela se calcula qué porcentaje de sus vecinos son del mismo color que él 145 | suficientes_iguales = vecin_prop * 100 >= self.intolerance # Se compara con la intolerancia, 146 | #si la proporción es mayor que su intol, está a gusto (True), si no, (False) 147 | no_demasiados_iguales = vecin_prop * 100 <= self.intomax # También se compara con la intol máxima, 148 | #si la proporción de gente igual alrededor es damasiado alta, tampoco está a gusto 149 | 150 | return suficientes_iguales and no_demasiados_iguales, vecin_prop 151 | 152 | 153 | #Sólo con estos objetos ya podemos ejecutar nuestras simulaciones, pero 154 | #nos será mucho más fácil si definimos unas cuantas funciones útiles: 155 | 156 | def crear_mundo(tam = 10, colors = 2, vacios = 20, intolerance = 30, intom = 100, 157 | prop = [0.5], rad = 1, h_fel = [], h_seg = []): 158 | '''Crea un nuevo espacio de análisis, con una matriz cuadrada de base. 159 | Variables: 160 | -tam: número de casillas del lado de la matriz. 161 | -colors: número de colores. 162 | -vacios: porcentaje de plazas vacías que quedarán en la matriz. 163 | -intolerance: porcentaje mínimo de vecinos adyacentes de su mismo color que admite 164 | un vecino antes de cambiar su posición. 165 | -intom: porcentaje máximo de vecinos adyacentes del mismo color que admite antes de mudarse. 166 | -prop: Proporción de cada color, es una lista con tantos elementos como colores menos uno. 167 | -rad: radio de análisis cuando se observan los vecinos de alguien. 168 | -h_fel: Aquí se irán guardando los valores de felicidad. 169 | -h_seg: Aquí, los de segregación.''' 170 | 171 | par = Parcela(tam, intolerance = intolerance, intomax = intom, r=rad) #Se crea una parcela nueva 172 | size = par.tot_pos 173 | 174 | llenos = (size * (100 - vacios))//100 #Se calculan cuantas casillas deben tener ocupantes. 175 | asignados = 0 176 | 177 | 178 | #A continuación, iremos añadiendo vecinos en la proporción deseada 179 | for color in range(1, colors): 180 | asignados_al_final = asignados + int(np.round(llenos * prop[color-1])) 181 | for i in range(asignados, asignados_al_final): 182 | par.nuevo(color) 183 | asignados = asignados_al_final 184 | for i in range(asignados, llenos + 1): 185 | par.nuevo(colors) 186 | 187 | #Lo último que hacemos es calcular el nivel de satisfacción y de segregación que 188 | #tiene esta distribución inicial 189 | tot_satis = 0 190 | tot_seg = 0 191 | for i in par.listavecinos : 192 | satis, prop_vec = i.satisfecho() 193 | tot_seg += prop_vec 194 | if satis: 195 | tot_satis +=1 196 | 197 | 198 | satisfaccion = 100 * tot_satis/len(par.listavecinos) #Es el porcentaje de la población que está a gusto 199 | segregacion = 100 * tot_seg/len(par.listavecinos) #Es la media del porcentaje de vecinos iguales a uno mismo. 200 | h_fel.append(np.round(satisfaccion,2)) 201 | h_seg.append(np.round(segregacion,2)) 202 | 203 | return par, (llenos, colors, prop) 204 | 205 | def vecin_print(par, datacolor, dim, n): 206 | '''Esta función sirve para dibujar la situación actual del vecindario. 207 | Necesita: 208 | -par: el objeto parcela del vecindario. 209 | -datacolor: una lista que contiene variables que explican cómo representar los datos. 210 | Esta lista es el segundo argumento de salida de la función "crear_mundo". 211 | -dim: el ancho de la matriz del mundo. 212 | -n: el número de la iteración''' 213 | 214 | 215 | 216 | colordict = {} #Aquí vamos a ir guardando las coordenadas de cada vecino agrupados por colores 217 | llenos, colors, prop = datacolor #desempaquetamos los datos de datacolor 218 | colornames = ['b', 'r', 'g', '0.4', 'c', 'k'] #Estos colores son los de los tipos de vecinos 219 | 220 | #Ahora, un bucle similar al que creó los vecinos los distribuirá por colores 221 | #para después poder pintarlos 222 | asignados = 0 223 | for color in range(1, colors): 224 | colorpop = [] 225 | asignados_al_final = asignados + int(np.round(llenos * prop[color-1])) 226 | for i in range(asignados, asignados_al_final): 227 | colorpop.append(par.listavecinos[i].coords) 228 | asignados = asignados_al_final 229 | colordict[color-1] = np.array(colorpop) 230 | colorpop = [] 231 | for i in range(asignados, llenos + 1): 232 | colorpop.append(par.listavecinos[i].coords) 233 | colordict[colors-1] = np.array(colorpop) 234 | 235 | 236 | 237 | 238 | plt.figure(None, figsize = (10,10)) 239 | plt.title('Vecindario: n =' + str(n)) 240 | tam_point = 105722 * dim ** -1.78 #El tamaño de los círculos se ajusta al tamaño de la matriz 241 | 242 | # Al representar el estado, también vamos a mostrar el nivel de satisfacción 243 | # y segregación, así que los calculamos. 244 | tot_satis = 0 245 | tot_seg = 0 246 | for i in par.listavecinos : 247 | satis, prop_vec = i.satisfecho() 248 | tot_seg += prop_vec 249 | if satis: 250 | tot_satis +=1 251 | else: 252 | plt.scatter(i.coords[0], i.coords[1], c = '0.6',marker = 's', s = tam_point, linewidths=0) 253 | 254 | satisfaccion = 100 * tot_satis/len(par.listavecinos) 255 | segregacion = 100 * tot_seg/len(par.listavecinos) 256 | nota = 'Satisfacción de la población: ' + str(np.round(satisfaccion, 2)) + '%' 257 | nota2 = 'Segregación de la población: ' + str(np.round(segregacion, 2)) + '%' 258 | 259 | #Lo pintamos todo: 260 | for i in range(colors): 261 | plt.scatter(colordict[i][:,0], colordict[i][:,1], c = colornames[i], s = tam_point, linewidths=0) 262 | 263 | plt.xlim(-1, dim) 264 | plt.ylim(-1, dim) 265 | 266 | plt.text(0, -1 - dim/20, nota, fontsize=12) 267 | plt.text(0, -1 - 2*dim/20, nota2,fontsize=12) 268 | 269 | 270 | def step_mudanza(par, datacolor, n, dim, h_fel = None, h_seg = None): 271 | '''Esta función recorre toda la lista de vecinos y los muda si no están satisfechos. 272 | después, guarda los valores conseguidos de felicidad y segregación, y por último 273 | representa todo gráficamente''' 274 | 275 | 276 | if not h_fel: h_fel = [] 277 | if not h_seg: h_seg = [] 278 | 279 | 280 | #Mudanza 281 | for i in par.listavecinos : 282 | if not i.satisfecho()[0]: 283 | i.mudanza() 284 | 285 | n += 1 286 | #Representación gráfica 287 | colordict = {} 288 | llenos, colors, prop = datacolor 289 | colornames = ['b', 'r', 'g', '0.4', 'c', 'k'] 290 | 291 | asignados = 0 292 | for color in range(1, colors): 293 | colorpop = [] 294 | asignados_al_final = asignados + int(np.round(llenos * prop[color-1])) 295 | for i in range(asignados, asignados_al_final): 296 | colorpop.append(par.listavecinos[i].coords) 297 | asignados = asignados_al_final 298 | colordict[color-1] = np.array(colorpop) 299 | colorpop = [] 300 | for i in range(asignados, llenos + 1): 301 | colorpop.append(par.listavecinos[i].coords) 302 | colordict[colors-1] = np.array(colorpop) 303 | 304 | plt.figure(None, figsize = (10,10)) 305 | tam_point = 105722 * dim ** -1.78 306 | 307 | tot_satis = 0 308 | tot_seg = 0 309 | for i in par.listavecinos : 310 | satis, prop_vec = i.satisfecho() 311 | tot_seg += prop_vec 312 | if satis: 313 | tot_satis +=1 314 | else: 315 | plt.scatter(i.coords[0], i.coords[1], c = '0.6',marker = 's', s = tam_point, linewidths=0) 316 | 317 | satisfaccion = 100 * tot_satis/len(par.listavecinos) 318 | segregacion = 100 * tot_seg/len(par.listavecinos) 319 | h_fel.append(np.round(satisfaccion,2)) 320 | h_seg.append(np.round(segregacion,2)) 321 | nota = 'Satisfacción de la población: ' + str(np.round(satisfaccion, 2)) + '%' 322 | nota2 = 'Segregación de la población: ' + str(np.round(segregacion, 2)) + '%' 323 | plt.title('Vecindario: n =' + str(n)) 324 | 325 | for i in range(colors): 326 | plt.scatter(colordict[i][:,0], colordict[i][:,1], c = colornames[i], s = tam_point, linewidths=0) 327 | plt.xlim(-1, dim ) 328 | plt.ylim(-1, dim ) 329 | plt.text(0, -1 - dim/20, nota, fontsize=12) 330 | plt.text(0, -1 - 2*dim/20, nota2,fontsize=12) 331 | return n 332 | 333 | 334 | def _step_mudanza_ciego(par, numcolor, n, dim, h_fel = None, h_seg = None): 335 | '''Esta función recorre la lista de vecinos y los muda si no están satisfechos, 336 | pero no los representa gráficamente.''' 337 | 338 | 339 | if not h_fel: h_fel = [] 340 | if not h_seg: h_seg = [] 341 | 342 | 343 | for i in par.listavecinos : 344 | if not i.satisfecho()[0]: 345 | i.mudanza() 346 | tot_satis = 0 347 | tot_seg = 0 348 | for i in par.listavecinos : 349 | satis, prop_vec = i.satisfecho() 350 | tot_seg += prop_vec 351 | if satis: 352 | tot_satis +=1 353 | 354 | satisfaccion = 100 * tot_satis/len(par.listavecinos) 355 | segregacion = 100 * tot_seg/len(par.listavecinos) 356 | h_fel.append(np.round(satisfaccion,2)) 357 | h_seg.append(np.round(segregacion,2)) 358 | return n + 1 359 | 360 | 361 | def step_multiple(par, datacolor, n, dim, h_fel = None, h_seg = None, numsteps = 20): 362 | '''Recorre la lista "numstep" veces, mudando a los vecinos infelices, y finalmente 363 | representa gráficamente el resultado''' 364 | 365 | if not h_fel: h_fel = [] 366 | if not h_seg: h_seg = [] 367 | 368 | print('Calculando steps, total', numsteps, ':', end=' ') 369 | for i in range(numsteps-1): 370 | n = _step_mudanza_ciego(par, datacolor, n, dim, h_fel, h_seg) 371 | print(i + 1 , end='·') 372 | 373 | print(numsteps) 374 | n = step_mudanza(par, datacolor, n, dim, h_fel, h_seg) 375 | return n 376 | 377 | def evolucion(h_fel, h_seg): 378 | '''Representa gráficamente la evolución de los valores 379 | de felicidad y segregación a lo largo de las iteraciones''' 380 | plt.figure(1, figsize= (8,5)) 381 | plt.title('Evolución de la felicidad del vecindario') 382 | plt.xlabel('Steps') 383 | plt.ylabel('Felicidad (%)') 384 | plt.plot(h_fel) 385 | plt.grid() 386 | 387 | plt.figure(2, figsize= (8,5)) 388 | plt.title('Evolución de la segregación del vecindario') 389 | plt.xlabel('Steps') 390 | plt.ylabel('Segregación (%)') 391 | plt.plot(h_seg) 392 | plt.grid() 393 | 394 | 395 | #Ejemplo: 396 | if __name__ == '__main__': 397 | 398 | 399 | 400 | dim = 20 401 | vacios = 10 402 | colors = 2 403 | prop = [0.5] 404 | n = 0 405 | intolerance = 35 406 | intom = 100 407 | rad = 1 408 | historia_felicidad = [] 409 | historia_segregacion = [] 410 | 411 | par, datacolor = crear_mundo(dim, colors= colors, prop= prop, vacios = vacios, 412 | intolerance=intolerance, intom= intom, rad = rad, 413 | h_fel = historia_felicidad, h_seg = historia_segregacion) 414 | 415 | vecin_print(par, datacolor, dim, n) 416 | plt.show() 417 | 418 | n = step_mudanza(par, datacolor, n, dim, 419 | historia_felicidad, historia_segregacion) 420 | plt.show() 421 | n = step_multiple(par, datacolor, n, dim, 422 | historia_felicidad, historia_segregacion, 15) 423 | plt.show() 424 | evolucion(historia_felicidad, historia_segregacion) 425 | plt.show() 426 | 427 | -------------------------------------------------------------------------------- /Bloque C - Ajuste y paquetes.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "![Aropython_logo](./static/aeropython_name_mini.png)\n", 8 | "###### Carlos Dorado Cárdenas\n", 9 | "###### Siro Moreno Martín" 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "# Simplifica tu vida con sistemas complejos y algoritmos genéticos" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "## Parte 3 - Ajuste de algoritmos y paquetes de Python" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "### 1. Influencia de los parámetros: bifurcaciones, cambios de fase y caos" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "En nuestros modelos, además de las propias variables de estudio, usaremos casi siempre varios parámetros para controlar su comprotamiento de manera fina. Normalmente, es de esperar que un cambio pequeño de un parámetro se traduzca en un cambio pequeño en el resultado, pero ¡cuidado! ¡No siempre es así!\n", 38 | "\n", 39 | "Llamamos **Bifurcación** a las situaciones en las que pequeños cambios en el valor de un parámetro afectan de manera drástica y cualitativa al comportamiento del sistema. \n", 40 | "\n", 41 | "Ejemplos: excitación de neuronas, formación de patrones, transición catastrófica de estados en un ecosistema." 42 | ] 43 | }, 44 | { 45 | "cell_type": "markdown", 46 | "metadata": {}, 47 | "source": [ 48 | "Supongamos el siguiente modelo de pocas variables en tiempo continuo:\n", 49 | "$$\\frac{dx}{dt} = r - x^2$$\n", 50 | "\n", 51 | "Nuestro parámetro, r afecta al comportamiento del sistema de manera drástica en el entorno de r = 0:" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": 1, 57 | "metadata": { 58 | "collapsed": true 59 | }, 60 | "outputs": [], 61 | "source": [ 62 | "%matplotlib inline\n", 63 | "import numpy as np\n", 64 | "import matplotlib.pyplot as plt" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": null, 70 | "metadata": { 71 | "collapsed": true 72 | }, 73 | "outputs": [], 74 | "source": [ 75 | "def derivada(x,r):\n", 76 | " return r - x **2" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": null, 82 | "metadata": { 83 | "collapsed": false 84 | }, 85 | "outputs": [], 86 | "source": [ 87 | "r = -1 #Para r negativo, la derivada de x con t siempre es negativo.\n", 88 | " #Para r positivo, aparecen puntos con derivada positiva o cero!\n", 89 | "x = np.linspace(-5,5,100)\n", 90 | "y = derivada(x, r)\n", 91 | "plt.plot(x,y)" 92 | ] 93 | }, 94 | { 95 | "cell_type": "markdown", 96 | "metadata": {}, 97 | "source": [ 98 | "Definamos ahora un modelo de tiempo discreto y pocas variables:\n", 99 | "$$ x_t = x_{t-1} + r - x_{t-1}^2 $$\n", 100 | "$$ x_0 = 0.1 $$" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": null, 106 | "metadata": { 107 | "collapsed": true 108 | }, 109 | "outputs": [], 110 | "source": [ 111 | "def iteracion(x, r):\n", 112 | " return x + r - x**2" 113 | ] 114 | }, 115 | { 116 | "cell_type": "code", 117 | "execution_count": null, 118 | "metadata": { 119 | "collapsed": false 120 | }, 121 | "outputs": [], 122 | "source": [ 123 | "x = 0.1\n", 124 | "r = 1.5 #Observa lo que ocurre en los alrededores de r = 1 y r = 1.5\n", 125 | "x_acc = [x,]\n", 126 | "for ii in range(30):\n", 127 | " x = iteracion(x, r)\n", 128 | " x_acc.append(x)\n", 129 | "plt.plot(x_acc)" 130 | ] 131 | }, 132 | { 133 | "cell_type": "markdown", 134 | "metadata": {}, 135 | "source": [ 136 | "Analicemos más en detalle lo que pasa con los periodos de este modelo en función de r! \n", 137 | "\n", 138 | "Para ello, descartaremos los primeros 100 pasos, que consideraremos transitorios, y pintaremos los resultados posteriores." 139 | ] 140 | }, 141 | { 142 | "cell_type": "code", 143 | "execution_count": null, 144 | "metadata": { 145 | "collapsed": false 146 | }, 147 | "outputs": [], 148 | "source": [ 149 | "plt.figure(figsize=[10,6])\n", 150 | "for r in np.linspace(0, 2, 200):\n", 151 | " x = 0.1\n", 152 | " x_acc = []\n", 153 | " for ii in range(100):\n", 154 | " x = iteracion(x, r)\n", 155 | " for ii in range(100):\n", 156 | " x = iteracion(x, r)\n", 157 | " x_acc.append(x)\n", 158 | " plt.plot([r,]*100,x_acc, 'b.', alpha = 0.3)" 159 | ] 160 | }, 161 | { 162 | "cell_type": "markdown", 163 | "metadata": {}, 164 | "source": [ 165 | "Las bifurcaciones que provoca el parámetro r se aprecian claramente, pero... ¿qué es esa zona extraña llena de puntos para r > 1.7 ?\n", 166 | "\n", 167 | "Eso... es Caos.\n", 168 | "\n", 169 | "---" 170 | ] 171 | }, 172 | { 173 | "cell_type": "markdown", 174 | "metadata": {}, 175 | "source": [ 176 | "\n", 177 | "\n", 178 | "\n", 179 | "**CAOS**\n", 180 | "* Es un comporrtamiento de sistemas dinámicos no lineales que nunca decae a trayectorias estables o periódicas.\n", 181 | "* Tiene un aspecto aleatorio, pero proviene de sistemas completamente deterministas.\n", 182 | "* Posee una gran sensibilidad a las condiciones iniciales.\n", 183 | "* Aparece cuando el periodo de la trayectoria diverge a infinito.\n", 184 | "* También aparece cuando no existe ninguna trayectoria estable.\n", 185 | "* Es un fenómeno que aparece en la naturaleza de manera muy frecuente, así como en entornos sociales e ingenieriles.\n", 186 | "\n", 187 | "*Fuente de la imagen: [sangre para el Dios de la Sangre!](http://warhammer40k.wikia.com/wiki/Chaos?file=Star_of_Chaos.jpg)*" 188 | ] 189 | }, 190 | { 191 | "cell_type": "code", 192 | "execution_count": null, 193 | "metadata": { 194 | "collapsed": false 195 | }, 196 | "outputs": [], 197 | "source": [ 198 | "plt.figure(figsize=[10,4])\n", 199 | "x = 0.1\n", 200 | "x_2 = x + 0.00000001\n", 201 | "r = 1.9\n", 202 | "x_acc = [x,]\n", 203 | "x_acc_2 = [x_2,]\n", 204 | "for ii in range(100):\n", 205 | " x = iteracion(x, r)\n", 206 | " x_acc.append(x)\n", 207 | " x_2 = iteracion(x_2, r)\n", 208 | " x_acc_2.append(x_2)\n", 209 | "plt.plot(x_acc)\n", 210 | "plt.plot(x_acc_2, 'g--')" 211 | ] 212 | }, 213 | { 214 | "cell_type": "markdown", 215 | "metadata": {}, 216 | "source": [ 217 | "Citando de nuevo a **Hiroki Sayama**:\n", 218 | "
Chaos can be understood as a dynamical process in which microscopic information hidden in the details of a system’s state is dug out and expanded to a macroscopically visible scale (stretching), while the macroscopic information visible in the current system’s state is continuously discarded (folding).\n", 219 | "
" 220 | ] 221 | }, 222 | { 223 | "cell_type": "markdown", 224 | "metadata": {}, 225 | "source": [ 226 | "#### 1.2 Cambio de fase" 227 | ] 228 | }, 229 | { 230 | "cell_type": "markdown", 231 | "metadata": {}, 232 | "source": [ 233 | "Pasemos a un modelo ligeramente más complicado: un autómata celular 2-D con el algoritmo de \"panico\":\n", 234 | "\n", 235 | "- Hay dos tipos de individuos: normales y en pánico.\n", 236 | "- Un individuo normal entrará en pánico si está rodeado de cuatro o más individuos en pánico.\n", 237 | "- Un individuo en pánico seguirá en pánico si está rodeado de tres o más personas en pánico. En caso contrario, se le pasará el susto y volvera al estado \"normal\".\n", 238 | "\n", 239 | "Condiciones del entrono del modelo:\n", 240 | "\n", 241 | "- Espacio 2-D cuadrado\n", 242 | "- Condición de contorno periódico (universo-toroide)\n", 243 | "- Distribución inicial aleatoria en la que cada individuo tiene una probabilidad *p* de estar en pánico.\n", 244 | "\n", 245 | "El profesor Hiroki ya nos ha regalado el código, que ejecutaremos en una terminal de Python (NO IPython!!)\n", 246 | "Este código se llama también \"droplet model\", debido a que se comporta de manera parecida a la condensación de gotas de agua!\n", 247 | "\n", 248 | "Observemos cómo el cambio del parámetro *p* provoca cambios radicales en el aspecto final del sistema dentro de un rango muy estrecho de valores: Esto se conoce como \"Cambio de Fase\" del sistema.\n", 249 | "\n", 250 | "Según Hiroki:\n", 251 | "\n", 252 | "
A phase transition is a transition of macroscopic properties of a collective system that occurs when its environmental or internal conditions are varied.\n", 253 | "
" 254 | ] 255 | }, 256 | { 257 | "cell_type": "markdown", 258 | "metadata": { 259 | "collapsed": true 260 | }, 261 | "source": [ 262 | "### 2. Presentación de los modelos de Redes" 263 | ] 264 | }, 265 | { 266 | "cell_type": "markdown", 267 | "metadata": { 268 | "collapsed": true 269 | }, 270 | "source": [ 271 | "Los modelos de redes para simular sistemas complejos son relativamente recientes, aunque se apoyan mucho en las teorías de grafos, una rama de las matemáticas muy anterior. Su popularidad como modelos de S.C. proviene principalmente de unos pocos papers de finales de los 90, que exploraban los conceptos de redes *small-world* y *scale-free networks*. Los modelos de redes son usados en biología, ecología, económicas, ingeniería, medicina y muchos otros campos. En la actualidad, su aplicación algorítmica en forma de redes neuronales se encuentra en fuerte expansión y goza de gran popularidad.\n", 272 | "\n", 273 | "* La principal ventaja de los modelos de redes frente a modelos más simples es su **flexibilidad** y su capacidad de definir modelos mucho más **detallados**. En los autómatas celulares nos encontrábamos con vecindarios rígidos y células homogéneas, pero con estos modelos podemos describir relaciones mucho más complicadas:\n", 274 | " * Algunos nodos pueden tener muchas comunicaciones, otros muy pocas o incluso ninguna.\n", 275 | " * Las relaciones entre nodos pueden ser no recíprocas, y contener características como \"peso relativo\", \"confianza\", etc.\n", 276 | " * Las conexiones entre nodos pueden cambiar con el tiempo, o incluso aparecer nodos nuevos o destruir nodos existentes.\n", 277 | "* Por contra, también tendremos que tener en cuenta los siguientes inconvenientes:\n", 278 | " * La cantidad de información inicial que tenemos que proporcionar al modelo es mucho mayor.\n", 279 | " * La cantidad de datos que se manejan en cualquier paso también es mucho mayor, por lo que podemos caer con facilidad en algoritmos absurdamente costosos o pesados.\n", 280 | " * La enorme cantidad de grados de libertad requiere de mucha planificación para conseguir un modelo real más allá de un bonito salvapantallas animado capaz de fundir procesadores." 281 | ] 282 | }, 283 | { 284 | "cell_type": "markdown", 285 | "metadata": { 286 | "collapsed": true 287 | }, 288 | "source": [ 289 | "#### 2.1 Terminología de teoría de grafos" 290 | ] 291 | }, 292 | { 293 | "cell_type": "markdown", 294 | "metadata": {}, 295 | "source": [ 296 | "* Una **Red** o **Grafo** consiste en una serie de **Nodos** (o **Vértices** o **Actores**) unidos entre sí mediante **Aristas**, (o **Enlaces** o **Uniones** o **Conexiones**)\n", 297 | "* El nodo *j* es un **vecino** del nodo *i* si y sólo si el nodo *i* está conectado al nodo *j*." 298 | ] 299 | }, 300 | { 301 | "cell_type": "markdown", 302 | "metadata": {}, 303 | "source": [ 304 | "\n", 305 | "\n", 306 | "* **Matriz de Adyacencias**: una matriz en la que el elemento $a_{ij}$ es 1 si el nodo *i* es vecino del nodo *j*, o cero si no.\n", 307 | "* **Lista de Adyacencias**: una lista cuyo componente i-ésimo es una lista de los vecinos del nodo *i*\n", 308 | "\n", 309 | " Fuente de la imágen: [Hiroki Sayama](http://polymer.bu.edu/hes/book-sayama.pdf)" 310 | ] 311 | }, 312 | { 313 | "cell_type": "markdown", 314 | "metadata": {}, 315 | "source": [ 316 | "* **Grado** de un nodo: el número de aristas conectadas al nodo.\n", 317 | "* Grafo **Completo**: todos los nodos están conectados entre sí.\n", 318 | "* Grafo **Regular**: todos los nodos tienen el mismo grado.\n", 319 | "* Un **camino** es una sucesión de vértices tal que de cada uno de sus vértices existe una arista hacia el vértice sucesor.\n", 320 | "* Un **Ciclo** (o circuito) es un camino que empieza y acaba en el mismo vértice.\n", 321 | "\n", 322 | "Puedes consultar un vocabulario más amplio en [Wikipedia](https://es.wikipedia.org/wiki/Anexo:Glosario_de_teor%C3%ADa_de_grafos)" 323 | ] 324 | }, 325 | { 326 | "cell_type": "markdown", 327 | "metadata": {}, 328 | "source": [ 329 | "### 3. Practicando Redes con NetworkX" 330 | ] 331 | }, 332 | { 333 | "cell_type": "markdown", 334 | "metadata": {}, 335 | "source": [ 336 | "NetworkX es un paquete de Python que viene instalado por defecto con anaconda y sirve para manejar grafos. Usaremos Matplotlib para las expandir las representaciones gráficas cuando sea necesario." 337 | ] 338 | }, 339 | { 340 | "cell_type": "code", 341 | "execution_count": 2, 342 | "metadata": { 343 | "collapsed": true 344 | }, 345 | "outputs": [], 346 | "source": [ 347 | "import networkx as nx" 348 | ] 349 | }, 350 | { 351 | "cell_type": "code", 352 | "execution_count": null, 353 | "metadata": { 354 | "collapsed": false 355 | }, 356 | "outputs": [], 357 | "source": [ 358 | "g = nx.karate_club_graph()\n", 359 | "nx.draw(g)" 360 | ] 361 | }, 362 | { 363 | "cell_type": "code", 364 | "execution_count": 8, 365 | "metadata": { 366 | "collapsed": false 367 | }, 368 | "outputs": [], 369 | "source": [ 370 | "g = nx.Graph()\n", 371 | "g.add_node('Arkantos')\n", 372 | "g.add_nodes_from(['Ayax', 'Quiron', 'Amanra', 'Ulises'])\n", 373 | "g.add_edge('Arkantos','Ayax')\n", 374 | "g.add_edges_from([\n", 375 | " ('Ayax','Quiron'),\n", 376 | " ('Ulises','Ayax'),\n", 377 | " ('Quiron','Ulises'),\n", 378 | " ('Amanra','Arkantos'),\n", 379 | " ('Amanra', 'Ayax'),\n", 380 | " ('Juan Luis Cano', 'Absurdeces Aleatorias')\n", 381 | " ])\n", 382 | "g.remove_edge('Ayax','Amanra')\n", 383 | "g.remove_node('Absurdeces Aleatorias')" 384 | ] 385 | }, 386 | { 387 | "cell_type": "code", 388 | "execution_count": null, 389 | "metadata": { 390 | "collapsed": true 391 | }, 392 | "outputs": [], 393 | "source": [ 394 | "nx.draw(g, with_labels=True)" 395 | ] 396 | }, 397 | { 398 | "cell_type": "code", 399 | "execution_count": null, 400 | "metadata": { 401 | "collapsed": false 402 | }, 403 | "outputs": [], 404 | "source": [ 405 | "g.nodes()" 406 | ] 407 | }, 408 | { 409 | "cell_type": "code", 410 | "execution_count": null, 411 | "metadata": { 412 | "collapsed": false 413 | }, 414 | "outputs": [], 415 | "source": [ 416 | "g.edges()" 417 | ] 418 | }, 419 | { 420 | "cell_type": "code", 421 | "execution_count": null, 422 | "metadata": { 423 | "collapsed": false 424 | }, 425 | "outputs": [], 426 | "source": [ 427 | "g.node" 428 | ] 429 | }, 430 | { 431 | "cell_type": "code", 432 | "execution_count": 11, 433 | "metadata": { 434 | "collapsed": true 435 | }, 436 | "outputs": [], 437 | "source": [ 438 | "g.node['Juan Luis Cano']['job'] = 'Python Master'\n", 439 | "g.node['Arkantos']['origin'] = 'Atlantis'\n", 440 | "g.node['Arkantos']['age'] = '40'" 441 | ] 442 | }, 443 | { 444 | "cell_type": "code", 445 | "execution_count": null, 446 | "metadata": { 447 | "collapsed": false, 448 | "scrolled": true 449 | }, 450 | "outputs": [], 451 | "source": [ 452 | "g.edge" 453 | ] 454 | }, 455 | { 456 | "cell_type": "code", 457 | "execution_count": 13, 458 | "metadata": { 459 | "collapsed": true 460 | }, 461 | "outputs": [], 462 | "source": [ 463 | "g.edge['Quiron']['Ayax']['Amistad'] = 'bastante'" 464 | ] 465 | }, 466 | { 467 | "cell_type": "markdown", 468 | "metadata": {}, 469 | "source": [ 470 | "NetworkX también admite grafos dirigidos:" 471 | ] 472 | }, 473 | { 474 | "cell_type": "code", 475 | "execution_count": null, 476 | "metadata": { 477 | "collapsed": false 478 | }, 479 | "outputs": [], 480 | "source": [ 481 | "g = nx.DiGraph()\n", 482 | "g.add_edge('yo', 'Sempai')\n", 483 | "g.edge ['yo'] ['Sempai'] ['Noticed'] = 'A lot! :D'\n", 484 | "g.edge ['Sempai'] ['yo']" 485 | ] 486 | }, 487 | { 488 | "cell_type": "code", 489 | "execution_count": null, 490 | "metadata": { 491 | "collapsed": false 492 | }, 493 | "outputs": [], 494 | "source": [ 495 | "nx.draw(g)" 496 | ] 497 | }, 498 | { 499 | "cell_type": "code", 500 | "execution_count": null, 501 | "metadata": { 502 | "collapsed": false 503 | }, 504 | "outputs": [], 505 | "source": [ 506 | "g = nx.karate_club_graph()\n", 507 | "plt.figure(figsize=[10,10])\n", 508 | "plt.subplot(221)\n", 509 | "nx.draw_random(g)\n", 510 | "\n", 511 | "plt.subplot(222)\n", 512 | "nx.draw_circular(g)\n", 513 | "\n", 514 | "plt.subplot(223)\n", 515 | "nx.draw_spectral(g)\n", 516 | "\n", 517 | "plt.subplot(224)\n", 518 | "shells = [\n", 519 | " [0,1,2,32,33],\n", 520 | " [3,5,6,7,8,13,23,27,29,30,31],\n", 521 | " [4,9,10,11,12,14,15,16,17,18,19,20,21,22,24,25,26,28]\n", 522 | "]\n", 523 | "nx.draw_shell(g, nlist = shells)" 524 | ] 525 | }, 526 | { 527 | "cell_type": "code", 528 | "execution_count": null, 529 | "metadata": { 530 | "collapsed": false 531 | }, 532 | "outputs": [], 533 | "source": [ 534 | "positions = nx.spring_layout(g)\n", 535 | "nx.draw(g,\n", 536 | " positions,\n", 537 | " with_labels = True,\n", 538 | " node_shape = '>',\n", 539 | " node_size = [g.degree(i)*50 for i in g.nodes()],\n", 540 | " edge_color = 'pink',\n", 541 | " node_color = ['yellow' if i<17 else 'green' for i in g.nodes()])" 542 | ] 543 | }, 544 | { 545 | "cell_type": "markdown", 546 | "metadata": {}, 547 | "source": [ 548 | "NetworkX también dispone de unas cuantas herramientas para crear grafos:" 549 | ] 550 | }, 551 | { 552 | "cell_type": "code", 553 | "execution_count": null, 554 | "metadata": { 555 | "collapsed": false 556 | }, 557 | "outputs": [], 558 | "source": [ 559 | "g = nx.complete_graph(6)\n", 560 | "nx.draw(g)" 561 | ] 562 | }, 563 | { 564 | "cell_type": "code", 565 | "execution_count": null, 566 | "metadata": { 567 | "collapsed": false, 568 | "scrolled": true 569 | }, 570 | "outputs": [], 571 | "source": [ 572 | "nx.draw(nx.gnm_random_graph(10,20))" 573 | ] 574 | }, 575 | { 576 | "cell_type": "code", 577 | "execution_count": null, 578 | "metadata": { 579 | "collapsed": true 580 | }, 581 | "outputs": [], 582 | "source": [ 583 | "nx.draw(nx.gnp_random_graph(20,0.1))" 584 | ] 585 | }, 586 | { 587 | "cell_type": "code", 588 | "execution_count": null, 589 | "metadata": { 590 | "collapsed": true 591 | }, 592 | "outputs": [], 593 | "source": [ 594 | "nx.draw(nx.random_regular_graph(3,10))" 595 | ] 596 | }, 597 | { 598 | "cell_type": "markdown", 599 | "metadata": {}, 600 | "source": [ 601 | "#### 3.1 Tipos de modelos de redes\n", 602 | "\n", 603 | "Hiroki Sayama propone dividir los modelos en 3 tipos:\n", 604 | "- Dinámicas *en* redes: Ni las conexiones ni los nodos cambian con el tiempo, sólo sus propiedades.\n", 605 | "- Dinámicas *de* redes: La topología cambia con el tiempo, pero no las propiedades de los nodos.\n", 606 | "- Redes adaptativas: La geometría de la red y los valores de sus propiedades cambian a la vez de manera interrelacionada.\n", 607 | "\n", 608 | "Podemos usar PyCX para probar un ejemplo de cada tipo:\n", 609 | "- Dinámicas en redes: net-voter\n", 610 | "- Dinámicas de redes: net-small-world\n", 611 | "- Redes adaptativas: net-epidemics-adaptive" 612 | ] 613 | }, 614 | { 615 | "cell_type": "markdown", 616 | "metadata": {}, 617 | "source": [ 618 | "### 4. Modelos basados en agentes\n", 619 | "\n", 620 | "Los modelos basados en agentes son los más generales de todos. Básicamente, el único requisito que tienen es que deben contener muchos agentes discretos. \n", 621 | "\n", 622 | "En palabras de Hiroki:\n", 623 | "\n", 624 | " Typical properties generally assumed in agents and ABMs\n", 625 | "\n", 626 | "- Agents are discrete entities.\n", 627 | "- Agents may have internal states.\n", 628 | "- Agents may be spatially localized.\n", 629 | "- Agents may perceive and interact with the environment.\n", 630 | "- Agents may behave based on predefined rules.\n", 631 | "- Agents may be able to learn and adapt.\n", 632 | "- Agents may interact with other agents.\n", 633 | "- ABMs often lack central supervisors/controllers.\n", 634 | "- ABMs may produce nontrivial “collective behavior” as a whole." 635 | ] 636 | }, 637 | { 638 | "cell_type": "markdown", 639 | "metadata": {}, 640 | "source": [ 641 | "Debido a la enorme libertad que tenemos a la hora de programar estos modelos, siempre es recomendable seguir uno de estos procedimientos:\n", 642 | "\n", 643 | "1. Construir el ABM usando condiciones y definiciones derivadas de fenomenos observados empíricamente, y para poder después producir situaciones nuevas desconocidas con la simulación.\n", 644 | "2. Construir el ABM usando hipótesis nuevas, y comparar nuestra simulación con resultados empíricos de fenómenos observados." 645 | ] 646 | }, 647 | { 648 | "cell_type": "markdown", 649 | "metadata": {}, 650 | "source": [ 651 | "El profesor Sayama también recomienda seguir la siguiente línea de procesos para el diseño de nuestro modelo:\n", 652 | "\n", 653 | "**Design tasks you need to do when you implement an ABM**\n", 654 | "1. Design the data structure to store the attributes of the agents.\n", 655 | "2. Design the data structure to store the states of the environment.\n", 656 | "3. Describe the rules for how the environment behaves on its own.\n", 657 | "4. Describe the rules for how agents interact with the environment.\n", 658 | "5. Describe the rules for how agents behave on their own.\n", 659 | "6. Describe the rules for how agents interact with each other.\n", 660 | "\n", 661 | "Comprobemos ahora ejemplos, con PyCX o de nuestros notebooks!" 662 | ] 663 | }, 664 | { 665 | "cell_type": "markdown", 666 | "metadata": {}, 667 | "source": [ 668 | "### 5. Cómo usar PyCX para prototipar modelos rápidamente" 669 | ] 670 | }, 671 | { 672 | "cell_type": "markdown", 673 | "metadata": {}, 674 | "source": [ 675 | "PyCX es un script diseñado por Hiroki Sayama y ampliado por Przemyslaw Szufel y Bogumil Kaminski de la Warsaw School of Economics.\n", 676 | "Nos permite añadir una capa de simulación y visualización a nuestro modelo de manera muy sencilla. \n", 677 | "\n", 678 | "Para usarlo, nuestro proyecto tiene que conservar la siguiente estructura:\n", 679 | "``` python\n", 680 | "import matplotlib\n", 681 | "matplotlib.use('qt4agg')\n", 682 | "import pylab as pl() \n", 683 | "\n", 684 | "# import necessary modules\n", 685 | "# define model parameters\n", 686 | "\n", 687 | "def initialize():\n", 688 | " global # list global variables\n", 689 | " # initialize system states\n", 690 | " \n", 691 | "def observe():\n", 692 | " global # list global variables\n", 693 | " cla() # to clear the visualization space\n", 694 | " # visualize system states\n", 695 | "\n", 696 | "def update():\n", 697 | " global # list global variables\n", 698 | " # update system states for one discrete time step\n", 699 | "\n", 700 | "import pycxsimulator\n", 701 | "pycxsimulator.GUI().start(func=[initialize, observe, update])\n", 702 | "```\n", 703 | "---\n", 704 | "Podemos descargar el script con numerosos ejemplos desde:\n", 705 | "http://pycx.sourceforge.net/WPyCX.html" 706 | ] 707 | }, 708 | { 709 | "cell_type": "markdown", 710 | "metadata": { 711 | "collapsed": true 712 | }, 713 | "source": [ 714 | "### 6. El paquete DEAP" 715 | ] 716 | }, 717 | { 718 | "cell_type": "markdown", 719 | "metadata": {}, 720 | "source": [ 721 | "DEAP, Distributed Evolutionary Algorithms for Python, es un paquete muy completo que según se puede leer en [la página del proyecto](http://deap.readthedocs.io/en/master/):\n", 722 | "
\n", 723 | "DEAP is a novel evolutionary computation framework for rapid prototyping and testing of ideas. It seeks to make algorithms explicit and data structures transparent. It works in perfect harmony with parallelisation mechanism such as multiprocessing and SCOOP. The following documentation presents the key concepts and many features to build your own evolutions.\n", 724 | "
" 725 | ] 726 | }, 727 | { 728 | "cell_type": "markdown", 729 | "metadata": { 730 | "collapsed": true 731 | }, 732 | "source": [ 733 | "DEAP aún no se encuentra disponible en el canal principal de conda, por lo que lo tenemos que descargar del canal oficial de prueba: conda-forge. Para ello, simplemente abre una terminal de comandos y escribe:\n", 734 | "```\n", 735 | "conda install --channel https://conda.anaconda.org/conda-forge deap\n", 736 | "```" 737 | ] 738 | }, 739 | { 740 | "cell_type": "markdown", 741 | "metadata": { 742 | "collapsed": true 743 | }, 744 | "source": [ 745 | "Probemos a ejecutar este ejemplo, disponible en su página: http://deap.readthedocs.io/en/master/examples/ga_onemax.html" 746 | ] 747 | }, 748 | { 749 | "cell_type": "code", 750 | "execution_count": 1, 751 | "metadata": { 752 | "collapsed": true 753 | }, 754 | "outputs": [], 755 | "source": [ 756 | "import random\n", 757 | "\n", 758 | "from deap import base\n", 759 | "from deap import creator\n", 760 | "from deap import tools" 761 | ] 762 | }, 763 | { 764 | "cell_type": "code", 765 | "execution_count": 2, 766 | "metadata": { 767 | "collapsed": true 768 | }, 769 | "outputs": [], 770 | "source": [ 771 | "creator.create(\"FitnessMax\", base.Fitness, weights=(1.0,))\n", 772 | "creator.create(\"Individual\", list, fitness=creator.FitnessMax)" 773 | ] 774 | }, 775 | { 776 | "cell_type": "code", 777 | "execution_count": 3, 778 | "metadata": { 779 | "collapsed": true 780 | }, 781 | "outputs": [], 782 | "source": [ 783 | "toolbox = base.Toolbox()\n", 784 | "# Attribute generator \n", 785 | "toolbox.register(\"attr_bool\", random.randint, 0, 1)\n", 786 | "# Structure initializers\n", 787 | "toolbox.register(\"individual\", tools.initRepeat, creator.Individual, \n", 788 | " toolbox.attr_bool, 100)\n", 789 | "toolbox.register(\"population\", tools.initRepeat, list, toolbox.individual)" 790 | ] 791 | }, 792 | { 793 | "cell_type": "code", 794 | "execution_count": 4, 795 | "metadata": { 796 | "collapsed": true 797 | }, 798 | "outputs": [], 799 | "source": [ 800 | "def evalOneMax(individual):\n", 801 | " return sum(individual)," 802 | ] 803 | }, 804 | { 805 | "cell_type": "code", 806 | "execution_count": 5, 807 | "metadata": { 808 | "collapsed": true 809 | }, 810 | "outputs": [], 811 | "source": [ 812 | "toolbox.register(\"evaluate\", evalOneMax)\n", 813 | "toolbox.register(\"mate\", tools.cxTwoPoint)\n", 814 | "toolbox.register(\"mutate\", tools.mutFlipBit, indpb=0.05)\n", 815 | "toolbox.register(\"select\", tools.selTournament, tournsize=3)" 816 | ] 817 | }, 818 | { 819 | "cell_type": "code", 820 | "execution_count": 14, 821 | "metadata": { 822 | "collapsed": false 823 | }, 824 | "outputs": [], 825 | "source": [ 826 | "def main():\n", 827 | " random.seed(64)\n", 828 | "\n", 829 | " # create an initial population of 300 individuals (where\n", 830 | " # each individual is a list of integers)\n", 831 | " pop = toolbox.population(n=300)\n", 832 | "\n", 833 | " # CXPB is the probability with which two individuals\n", 834 | " # are crossed\n", 835 | " #\n", 836 | " # MUTPB is the probability for mutating an individual\n", 837 | " #\n", 838 | " # NGEN is the number of generations for which the\n", 839 | " # evolution runs\n", 840 | " CXPB, MUTPB, NGEN = 0.5, 0.2, 40\n", 841 | " \n", 842 | " print(\"Start of evolution\")\n", 843 | " \n", 844 | " # Evaluate the entire population\n", 845 | " fitnesses = list(map(toolbox.evaluate, pop))\n", 846 | " for ind, fit in zip(pop, fitnesses):\n", 847 | " ind.fitness.values = fit\n", 848 | " \n", 849 | " print(\" Evaluated %i individuals\" % len(pop))\n", 850 | " \n", 851 | " # Begin the evolution\n", 852 | " for g in range(NGEN):\n", 853 | " print(\"-- Generation %i --\" % g)\n", 854 | " \n", 855 | " # Select the next generation individuals\n", 856 | " offspring = toolbox.select(pop, len(pop))\n", 857 | " # Clone the selected individuals\n", 858 | " offspring = list(map(toolbox.clone, offspring))\n", 859 | " \n", 860 | " # Apply crossover and mutation on the offspring\n", 861 | " for child1, child2 in zip(offspring[::2], offspring[1::2]):\n", 862 | "\n", 863 | " # cross two individuals with probability CXPB\n", 864 | " if random.random() < CXPB:\n", 865 | " toolbox.mate(child1, child2)\n", 866 | "\n", 867 | " # fitness values of the children\n", 868 | " # must be recalculated later\n", 869 | " del child1.fitness.values\n", 870 | " del child2.fitness.values\n", 871 | "\n", 872 | " for mutant in offspring:\n", 873 | "\n", 874 | " # mutate an individual with probability MUTPB\n", 875 | " if random.random() < MUTPB:\n", 876 | " toolbox.mutate(mutant)\n", 877 | " del mutant.fitness.values\n", 878 | " \n", 879 | " # Evaluate the individuals with an invalid fitness\n", 880 | " invalid_ind = [ind for ind in offspring if not ind.fitness.valid]\n", 881 | " fitnesses = map(toolbox.evaluate, invalid_ind)\n", 882 | " for ind, fit in zip(invalid_ind, fitnesses):\n", 883 | " ind.fitness.values = fit\n", 884 | " \n", 885 | " print(\" Evaluated %i individuals\" % len(invalid_ind))\n", 886 | " \n", 887 | " # The population is entirely replaced by the offspring\n", 888 | " pop[:] = offspring\n", 889 | " \n", 890 | " # Gather all the fitnesses in one list and print the stats\n", 891 | " fits = [ind.fitness.values[0] for ind in pop]\n", 892 | " \n", 893 | " length = len(pop)\n", 894 | " mean = sum(fits) / length\n", 895 | " sum2 = sum(x*x for x in fits)\n", 896 | " std = abs(sum2 / length - mean**2)**0.5\n", 897 | " \n", 898 | " print(\" Min %s\" % min(fits))\n", 899 | " print(\" Max %s\" % max(fits))\n", 900 | " print(\" Avg %s\" % mean)\n", 901 | " print(\" Std %s\" % std)\n", 902 | " \n", 903 | " print(\"-- End of (successful) evolution --\")\n", 904 | " \n", 905 | " best_ind = tools.selBest(pop, 1)[0]\n", 906 | " print(\"Best individual is %s, %s\" % (best_ind, best_ind.fitness.values))" 907 | ] 908 | }, 909 | { 910 | "cell_type": "code", 911 | "execution_count": null, 912 | "metadata": { 913 | "collapsed": false 914 | }, 915 | "outputs": [], 916 | "source": [ 917 | "main()" 918 | ] 919 | }, 920 | { 921 | "cell_type": "markdown", 922 | "metadata": {}, 923 | "source": [ 924 | "### Ejecución de ejemplos, preguntas..." 925 | ] 926 | }, 927 | { 928 | "cell_type": "markdown", 929 | "metadata": { 930 | "collapsed": true 931 | }, 932 | "source": [ 933 | "Si te ha gustado este taller, háznoslo saber!" 934 | ] 935 | }, 936 | { 937 | "cell_type": "code", 938 | "execution_count": 3, 939 | "metadata": { 940 | "collapsed": false 941 | }, 942 | "outputs": [ 943 | { 944 | "data": { 945 | "text/html": [ 946 | "Tweet\n", 947 | "" 948 | ], 949 | "text/plain": [ 950 | "" 951 | ] 952 | }, 953 | "metadata": {}, 954 | "output_type": "display_data" 955 | } 956 | ], 957 | "source": [ 958 | "%%html\n", 959 | "Tweet\n", 960 | "" 961 | ] 962 | }, 963 | { 964 | "cell_type": "markdown", 965 | "metadata": {}, 966 | "source": [ 967 | "Siro Moreno, Aeropython, 7 de Octubre de 2016\n" 968 | ] 969 | }, 970 | { 971 | "cell_type": "code", 972 | "execution_count": 2, 973 | "metadata": { 974 | "collapsed": false 975 | }, 976 | "outputs": [ 977 | { 978 | "data": { 979 | "text/html": [ 980 | "\n", 981 | "\n", 982 | "\n", 983 | "\n", 1106 | "\n" 1122 | ], 1123 | "text/plain": [ 1124 | "" 1125 | ] 1126 | }, 1127 | "execution_count": 2, 1128 | "metadata": {}, 1129 | "output_type": "execute_result" 1130 | } 1131 | ], 1132 | "source": [ 1133 | "# Notebook style\n", 1134 | "from IPython.core.display import HTML\n", 1135 | "css_file = './static/style.css'\n", 1136 | "HTML(open(css_file, \"r\").read())" 1137 | ] 1138 | }, 1139 | { 1140 | "cell_type": "code", 1141 | "execution_count": null, 1142 | "metadata": { 1143 | "collapsed": true 1144 | }, 1145 | "outputs": [], 1146 | "source": [] 1147 | } 1148 | ], 1149 | "metadata": { 1150 | "kernelspec": { 1151 | "display_name": "Python 3", 1152 | "language": "python", 1153 | "name": "python3" 1154 | }, 1155 | "language_info": { 1156 | "codemirror_mode": { 1157 | "name": "ipython", 1158 | "version": 3 1159 | }, 1160 | "file_extension": ".py", 1161 | "mimetype": "text/x-python", 1162 | "name": "python", 1163 | "nbconvert_exporter": "python", 1164 | "pygments_lexer": "ipython3", 1165 | "version": "3.5.2" 1166 | } 1167 | }, 1168 | "nbformat": 4, 1169 | "nbformat_minor": 0 1170 | } 1171 | --------------------------------------------------------------------------------