├── vis ├── __init__.py ├── videos │ ├── ackley_firefly.mp4 │ └── mich_firefly.mp4 ├── Figures │ ├── iteration_26500.png │ ├── Benchmark_xqf131_.png │ ├── benchmark_xqf131.png │ └── convergence_xqf131.png ├── tsp_tools.py ├── check_on_ackley.py └── check_on_michalewicz.py ├── requirements.txt ├── data_utils ├── __init__.py ├── data │ ├── xqf131.tsp_matrix.npy │ ├── xqf131.tsp │ ├── xqg237.tsp │ └── pma343.tsp ├── data_generator_.py └── read_input_files_.py ├── presentation.pptx ├── firefly_optimisation ├── __init__.py ├── benchmark_functions_.py └── firefly_.py ├── GA_tsp_optimisation ├── __init__.py ├── selection.py ├── mutation.py ├── crossover.py └── ga_pipeline.py ├── experiments ├── firefly_algorithm.py ├── hyperopt_optim.py └── ga_tsp_experiments.py ├── README.md ├── LICENSE └── .gitignore /vis/__init__.py: -------------------------------------------------------------------------------- 1 | from vis.tsp_tools import * 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | hyperopt 2 | numpy 3 | matplotlib 4 | -------------------------------------------------------------------------------- /data_utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .data_generator_ import * 2 | from .read_input_files_ import * -------------------------------------------------------------------------------- /presentation.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngc436/OptimisationAlgorithms/HEAD/presentation.pptx -------------------------------------------------------------------------------- /firefly_optimisation/__init__.py: -------------------------------------------------------------------------------- 1 | from .benchmark_functions_ import * 2 | from .firefly_ import FireflyOptimizer 3 | -------------------------------------------------------------------------------- /vis/videos/ackley_firefly.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngc436/OptimisationAlgorithms/HEAD/vis/videos/ackley_firefly.mp4 -------------------------------------------------------------------------------- /vis/videos/mich_firefly.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngc436/OptimisationAlgorithms/HEAD/vis/videos/mich_firefly.mp4 -------------------------------------------------------------------------------- /vis/Figures/iteration_26500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngc436/OptimisationAlgorithms/HEAD/vis/Figures/iteration_26500.png -------------------------------------------------------------------------------- /vis/Figures/Benchmark_xqf131_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngc436/OptimisationAlgorithms/HEAD/vis/Figures/Benchmark_xqf131_.png -------------------------------------------------------------------------------- /vis/Figures/benchmark_xqf131.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngc436/OptimisationAlgorithms/HEAD/vis/Figures/benchmark_xqf131.png -------------------------------------------------------------------------------- /vis/Figures/convergence_xqf131.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngc436/OptimisationAlgorithms/HEAD/vis/Figures/convergence_xqf131.png -------------------------------------------------------------------------------- /data_utils/data/xqf131.tsp_matrix.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngc436/OptimisationAlgorithms/HEAD/data_utils/data/xqf131.tsp_matrix.npy -------------------------------------------------------------------------------- /GA_tsp_optimisation/__init__.py: -------------------------------------------------------------------------------- 1 | from .mutation import Mutation 2 | from .crossover import Crossover 3 | from .selection import Selector 4 | from .ga_pipeline import ga_pipeline -------------------------------------------------------------------------------- /experiments/firefly_algorithm.py: -------------------------------------------------------------------------------- 1 | from firefly_optimisation import FireflyOptimizer 2 | 3 | f_alg = FireflyOptimizer(population_size=50, problem_dim=2, generations=100, beta_min=0.65, alpha=0.05) 4 | f_alg.run_firefly() 5 | -------------------------------------------------------------------------------- /data_utils/data_generator_.py: -------------------------------------------------------------------------------- 1 | from numpy.random import random_sample 2 | 3 | 4 | def generate_population(population_size, problem_dim, min_bound, max_bound): 5 | error = 1e-10 6 | data = (max_bound + error - min_bound) * random_sample((population_size, problem_dim)) + min_bound 7 | data[data > max_bound] = max_bound 8 | return data 9 | -------------------------------------------------------------------------------- /experiments/hyperopt_optim.py: -------------------------------------------------------------------------------- 1 | # 200 dim optimization 2 | 3 | from firefly_optimisation import FireflyOptimizer 4 | from hyperopt import STATUS_OK, fmin, hp, tpe 5 | 6 | 7 | def score(params): 8 | f_alg = FireflyOptimizer(**params) 9 | fitness, position = f_alg.run_firefly() 10 | return {'loss': -fitness, 'status': STATUS_OK} 11 | 12 | 13 | def optimize(): 14 | space = { 15 | 'population_size': hp.quniform('population_size', 10, 200, 1), 16 | 'gamma': hp.quniform('gamma', 0.5, 1, 0.05), 17 | 'alpha': hp.quniform('alpha', 0, 1, 0.05), 18 | 'beta_min': hp.quniform('beta_min', 0, 1, 0.05), 19 | # optimize for the below params 20 | 'problem_dim': 200, 21 | 'generations': 50 22 | } 23 | best = fmin(score, space, algo=tpe.suggest, max_evals=100) 24 | return best 25 | 26 | best_hyperparams = optimize() 27 | print(best_hyperparams) 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Firefly algorithm 2 | 3 | Python implementation of the metaheuristic Firefly algorithm. 4 | 5 | - [x] Implement algorithm 6 | - [x] Add several benchmarks 7 | - [x] Add examples 8 | - [x] Prepare convergence animation 9 | - [x] Upload presentation 10 | 11 | #### Literature 12 | 13 | 1. Xin-She Yang and Xingshi He, (2013). ‘Firefly Algorithm: Recent Advances and Applications’, Int. J. Swarm Intelligence, 14 | Vol. 1, No. 1, pp. 36–50. DOI: 10.1504/IJSI.2013.055801. 15 | 2. X. S. Yang, Firefly algorithms for multimodal optimisation, Proc. 5th Symposium 16 | on Stochastic Algorithms, Foundations and Applications, (Eds. O. Watanabe and T. 17 | Zeugmann), Lecture Notes in Computer Science, 5792: 169-178 (2009). 18 | 19 | # TSP problem solving with Genetic Algorithm 20 | 21 | Python implementation Genetic Algorithm for solving Travelling Salesman Problem. 22 | 23 | - [x] Implement basic pipeline 24 | - [ ] Check on several tsp benchmarks 25 | - [x] Tune parameters 26 | - [x] Prepare results visualization 27 | -------------------------------------------------------------------------------- /data_utils/read_input_files_.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import os 3 | from scipy.spatial.distance import euclidean 4 | 5 | 6 | def read_tsp_file(fname): 7 | with open(os.path.dirname(__file__) + '/data/%s' % fname) as f: 8 | while f.readline() != 'TYPE : TSP\n': 9 | pass 10 | points_count = int(f.readline().split(':')[1]) 11 | while f.readline() != 'NODE_COORD_SECTION\n': 12 | pass 13 | coord = np.zeros(shape=(points_count, 2)) 14 | for i in range(points_count): 15 | coord[i] = f.readline().split()[1:] 16 | return coord 17 | 18 | 19 | def create_matrix(fname): 20 | coord = read_tsp_file(fname) 21 | full_name = os.path.dirname(__file__) + '/data/%s_matrix.npy' % fname.split()[0] 22 | try: 23 | matrix = np.load(full_name) 24 | except IOError: 25 | matrix = np.zeros((len(coord), len(coord))) 26 | for i in range(len(coord)): 27 | for j in range(len(coord)): 28 | matrix[i][j] = euclidean(coord[i], coord[j]) 29 | np.save(full_name, matrix) 30 | return matrix 31 | -------------------------------------------------------------------------------- /vis/tsp_tools.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import re 3 | import os 4 | 5 | 6 | def plot_points(coords, title='Benchmark'): 7 | plt.scatter(coords[:, 0], coords[:, 1], s=8, marker=".") 8 | plt.title(title) 9 | plt.show() 10 | plt.savefig('%s\\%s\\%s' % (os.path.dirname(__file__), 'Figures', re.sub(r'(\.[A-Za-z0-9]+)| ', "_", title))) 11 | 12 | 13 | def draw_path(path, coords, iteration): 14 | for i in range(len(path) - 1): 15 | x = [coords[path[i]][0], coords[path[i + 1]][0]] 16 | y = [coords[path[i]][1], coords[path[i + 1]][1]] 17 | plt.plot(x, y, marker='o', markersize=2) 18 | plt.plot([coords[path[len(path)-1]][0], coords[path[0]][0]], [coords[path[len(path)-1]][1], coords[path[0]][1]], marker='o', markersize=2) 19 | plt.title('Mininmal path on %s iteration' % iteration) 20 | plt.xlabel('x') 21 | plt.ylabel('y') 22 | plt.show() 23 | 24 | 25 | def draw_convergence(x_list, y_list, params): 26 | plt.plot(x_list, y_list) 27 | plt.xlabel('Iteration') 28 | plt.ylabel('Minimal distance') 29 | plt.title('GA convergence %s'%params) 30 | plt.show() 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Maria Khodorchenko 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 | -------------------------------------------------------------------------------- /GA_tsp_optimisation/selection.py: -------------------------------------------------------------------------------- 1 | import operator 2 | import random 3 | 4 | 5 | class Selector: 6 | def __init__(self, selection_type): 7 | assert selection_type in ['roulette', 'tournament'] 8 | self.selection_type = selection_type 9 | 10 | def selection(self, **kwargs): 11 | if self.selection_type == 'roulette': 12 | return self._roulette(**kwargs) 13 | if self.selection_type == 'tournament': 14 | return self._tournament(**kwargs) 15 | 16 | def yield_mating_pairs(self, pairs, population): 17 | for j in range(pairs): 18 | chosen = [] 19 | for _ in range(2): 20 | selection_probability = random.random() 21 | for individ in population: 22 | if selection_probability >= individ._prob: 23 | chosen.append(individ) 24 | break 25 | if len(chosen) < 2: 26 | chosen.append(population[-1]) 27 | if len(chosen) == 1: 28 | chosen.append(population[-2]) 29 | yield chosen[0], chosen[1] 30 | 31 | def _roulette(self, **kwargs): 32 | population = kwargs.get('population') 33 | best_population = kwargs.get('best_perc') 34 | cumsum_fitness = 0 35 | for individ in population: 36 | cumsum_fitness += individ.fitness 37 | individ._prob = individ.fitness / cumsum_fitness 38 | return self.yield_mating_pairs(int(len(population) - int(len(population) * best_population))//2 + 1, population) 39 | 40 | def _tournament(self, **kwargs): 41 | raise NotImplementedError 42 | -------------------------------------------------------------------------------- /vis/check_on_ackley.py: -------------------------------------------------------------------------------- 1 | from matplotlib import pyplot as plt 2 | from matplotlib import cm 3 | from matplotlib import animation 4 | from optimisation import FireflyOptimizer 5 | import numpy as np 6 | from optimisation import Ackley 7 | 8 | 9 | f_alg = FireflyOptimizer(population_size=10, problem_dim=2, generations=100) 10 | func = Ackley(2) 11 | 12 | N = 100 13 | x = np.linspace(-5, 5, N) 14 | y = np.linspace(-5, 5, N) 15 | X, Y = np.meshgrid(x, y) 16 | z = func.get_y_2d(X, Y) 17 | # dt = 1. / 30 18 | 19 | fig = plt.figure() 20 | fig.subplots_adjust(left=0, right=1, bottom=0, top=1) 21 | ax = fig.add_subplot(111, aspect='equal', xlim=(-5, 5), ylim=(-5, 5)) # autoscale_on=False) 22 | cs = ax.contourf(X, Y, z, cmap=cm.PuBu_r) 23 | cbar = fig.colorbar(cs) 24 | 25 | particles, = ax.plot([], [], 'bo', ms=6) 26 | 27 | rect = plt.Rectangle([-5, 5], 10, 10, ec='none', lw=2, fc='none') 28 | ax.add_patch(rect) 29 | 30 | def init(): 31 | global f_alg, rect 32 | particles.set_data([], []) 33 | rect.set_edgecolor('none') 34 | return particles, rect 35 | 36 | def animate(i): 37 | global f_alg, rect, ax, fig 38 | ms = int(fig.dpi * 2 * 0.04 * fig.get_figwidth() 39 | / np.diff(ax.get_xbound())[0]) 40 | rect.set_edgecolor('k') 41 | x = [] 42 | y = [] 43 | for ind in f_alg.population: 44 | x.append(ind.position[0]) 45 | y.append(ind.position[1]) 46 | f_alg.step() 47 | particles.set_data(x, y) 48 | particles.set_markersize(ms) 49 | return particles, rect 50 | 51 | ani = animation.FuncAnimation(fig, animate, frames=200, interval=10, 52 | blit=True, init_func=init) 53 | ani.save('videos/ackley_firefly.mp4', fps=5, extra_args=['-vcodec', 'libx264']) 54 | 55 | plt.show() 56 | 57 | 58 | #animate_firefly_convergence() 59 | -------------------------------------------------------------------------------- /vis/check_on_michalewicz.py: -------------------------------------------------------------------------------- 1 | from optimisation import Michalewicz 2 | import numpy as np 3 | from matplotlib import pyplot as plt 4 | from matplotlib import cm 5 | from optimisation import FireflyOptimizer 6 | from matplotlib import animation 7 | 8 | f_alg = FireflyOptimizer(population_size=40, problem_dim=2, generations=100, 9 | min_bound=0, max_bound=np.pi) 10 | 11 | func = Michalewicz(2) 12 | 13 | N = 100 14 | x = np.linspace(0, np.pi, N) 15 | y = np.linspace(0, np.pi, N) 16 | X, Y = np.meshgrid(x, y) 17 | z = func.get_y_2d(X, Y) 18 | 19 | fig = plt.figure() 20 | fig.subplots_adjust(left=0, right=1, bottom=0, top=1) 21 | ax = fig.add_subplot(111, aspect='equal', xlim=(0, np.pi), ylim=(0, np.pi)) # autoscale_on=False) 22 | cs = ax.contourf(X, Y, z, cmap=cm.PuBu_r) 23 | cbar = fig.colorbar(cs) 24 | 25 | particles, = ax.plot([], [], 'bo', ms=6) 26 | 27 | rect = plt.Rectangle([0, np.pi], np.pi, np.pi, ec='none', lw=2, fc='none') 28 | ax.add_patch(rect) 29 | 30 | def init(): 31 | global f_alg, rect 32 | particles.set_data([], []) 33 | rect.set_edgecolor('none') 34 | return particles, rect 35 | 36 | def animate(i): 37 | global f_alg, rect, ax, fig 38 | ms = int(fig.dpi * 2 * 0.004 * fig.get_figwidth() 39 | / np.diff(ax.get_xbound())[0]) 40 | rect.set_edgecolor('k') 41 | x = [] 42 | y = [] 43 | for ind in f_alg.population: 44 | x.append(ind.position[0]) 45 | y.append(ind.position[1]) 46 | f_alg.step() 47 | particles.set_data(x, y) 48 | particles.set_markersize(ms) 49 | return particles, rect 50 | 51 | ani = animation.FuncAnimation(fig, animate, frames=200, interval=2, 52 | blit=True, init_func=init) 53 | ani.save('videos/mich_firefly.mp4', fps=5, extra_args=['-vcodec', 'libx264']) 54 | 55 | plt.show() 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | .idea 107 | 108 | /sandbox 109 | /results 110 | -------------------------------------------------------------------------------- /experiments/ga_tsp_experiments.py: -------------------------------------------------------------------------------- 1 | from data_utils import create_matrix, read_tsp_file 2 | from GA_tsp_optimisation import ga_pipeline 3 | from hyperopt import fmin, tpe, hp, STATUS_OK, Trials 4 | 5 | fname = 'xqf131.tsp' 6 | 7 | matrix = create_matrix(fname) 8 | coord = read_tsp_file(fname) 9 | 10 | # 564 11 | 12 | def ga_wrapper(params): 13 | population_size = int(params['population_size']) 14 | best_perc = params['best_perc'] 15 | mutation_rate = params['mutation_rate'] 16 | val = ga_pipeline(mat=matrix, population_size=population_size, 17 | best_perc=best_perc, mutation_rate=mutation_rate, 18 | generations=20000, verbose=0, plot=0) 19 | print(params, val) 20 | return {'loss': val, 'status': STATUS_OK} 21 | 22 | 23 | def hyperopt_optimization(): 24 | trials = Trials() 25 | 26 | space = { 27 | 'best_perc': hp.uniform('best_perc', 0.1, 0.9), 28 | 'population_size': hp.quniform('population_size', 20, 50, 1), 29 | 'mutation_rate': hp.uniform('mutation_rate', 0.1, 0.8) 30 | } 31 | 32 | best = fmin( 33 | ga_wrapper, 34 | space=space, 35 | algo=tpe.suggest, 36 | max_evals=50, 37 | trials=trials 38 | ) 39 | 40 | return best 41 | 42 | 43 | def run_tsp(population_size, best_perc, mutation_probability, generations, coord): 44 | val = ga_pipeline(mat=matrix, population_size=population_size, 45 | best_perc=best_perc, mutation_probability=mutation_probability, 46 | generations=generations, verbose=1, coord=coord, plot=1) 47 | return val 48 | 49 | 50 | def main(): 51 | # print(hyperopt_optimization()) 52 | print( 53 | run_tsp(mutation_probability=0.4, best_perc=0.3, 54 | population_size=40, generations=5000, coord=coord)) 55 | 56 | 57 | if __name__ == '__main__': 58 | main() 59 | -------------------------------------------------------------------------------- /GA_tsp_optimisation/mutation.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import random 3 | 4 | 5 | class Mutation: 6 | def __init__(self, mutation_type, **kwargs): 7 | self.mutation_type = mutation_type 8 | 9 | def mutation(self, individ, **kwargs): 10 | if self.mutation_type == 'rsm': 11 | return self.rsm(individ=individ, **kwargs) 12 | if self.mutation_type == 'swap': 13 | return self.swap_mutation(individ=individ, **kwargs) 14 | if self.mutation_type == 'psm': 15 | return self.psm(individ=individ, **kwargs) 16 | 17 | def rsm(self, **kwargs): 18 | individ = kwargs.get('individ') 19 | mutation_rate = kwargs.get('mutation_probability') 20 | if random.random() > mutation_rate: 21 | return individ 22 | ix_len = 0 23 | while ix_len < 1: 24 | ix = np.random.choice(len(individ) + 1, 2, replace=False) 25 | ix_len = len(ix) 26 | sub = individ[min(ix): max(ix)] 27 | sub = sub[::-1] 28 | individ[min(ix): max(ix)] = sub 29 | return individ 30 | 31 | def psm(self, **kwargs): 32 | individ = kwargs.get('individ') 33 | mutation_probability = kwargs.get("mutation_probability") 34 | for i in range(len(individ)): 35 | if random.random() < mutation_probability: 36 | j = int(random.random() * (len(individ) - 1)) 37 | tmp = individ[i] 38 | individ[i] = individ[j] 39 | individ[j] = tmp 40 | return individ 41 | 42 | def swap_mutation(self, **kwargs): 43 | individ = kwargs.get('individ') 44 | mutation_intensity = kwargs.get('mutation_intensity') 45 | for _ in range(int(len(individ) * mutation_intensity)): 46 | if random.random() > 0.5: 47 | ix = np.random.choice(len(individ), 2, replace=False) 48 | tmp = individ[ix[0]] 49 | individ[ix[0]] = individ[ix[1]] 50 | individ[ix[1]] = tmp 51 | return individ 52 | -------------------------------------------------------------------------------- /data_utils/data/xqf131.tsp: -------------------------------------------------------------------------------- 1 | NAME : xqf131 2 | COMMENT : Bonn VLSI data set with 131 points 3 | COMMENT : Uni Bonn, Research Institute for Discrete Math 4 | COMMENT : Contributed by Andre Rohe 5 | TYPE : TSP 6 | DIMENSION : 131 7 | EDGE_WEIGHT_TYPE : EUC_2D 8 | NODE_COORD_SECTION 9 | 1 0 13 10 | 2 0 26 11 | 3 0 27 12 | 4 0 39 13 | 5 2 0 14 | 6 5 13 15 | 7 5 19 16 | 8 5 25 17 | 9 5 31 18 | 10 5 37 19 | 11 5 43 20 | 12 5 8 21 | 13 8 0 22 | 14 9 10 23 | 15 10 10 24 | 16 11 10 25 | 17 12 10 26 | 18 12 5 27 | 19 15 13 28 | 20 15 19 29 | 21 15 25 30 | 22 15 31 31 | 23 15 37 32 | 24 15 43 33 | 25 15 8 34 | 26 18 11 35 | 27 18 13 36 | 28 18 15 37 | 29 18 17 38 | 30 18 19 39 | 31 18 21 40 | 32 18 23 41 | 33 18 25 42 | 34 18 27 43 | 35 18 29 44 | 36 18 31 45 | 37 18 33 46 | 38 18 35 47 | 39 18 37 48 | 40 18 39 49 | 41 18 41 50 | 42 18 42 51 | 43 18 44 52 | 44 18 45 53 | 45 25 11 54 | 46 25 15 55 | 47 25 22 56 | 48 25 23 57 | 49 25 24 58 | 50 25 26 59 | 51 25 28 60 | 52 25 29 61 | 53 25 9 62 | 54 28 16 63 | 55 28 20 64 | 56 28 28 65 | 57 28 30 66 | 58 28 34 67 | 59 28 40 68 | 60 28 43 69 | 61 28 47 70 | 62 32 26 71 | 63 32 31 72 | 64 33 15 73 | 65 33 26 74 | 66 33 29 75 | 67 33 31 76 | 68 34 15 77 | 69 34 26 78 | 70 34 29 79 | 71 34 31 80 | 72 34 38 81 | 73 34 41 82 | 74 34 5 83 | 75 35 17 84 | 76 35 31 85 | 77 38 16 86 | 78 38 20 87 | 79 38 30 88 | 80 38 34 89 | 81 40 22 90 | 82 41 23 91 | 83 41 32 92 | 84 41 34 93 | 85 41 35 94 | 86 41 36 95 | 87 48 22 96 | 88 48 27 97 | 89 48 6 98 | 90 51 45 99 | 91 51 47 100 | 92 56 25 101 | 93 57 12 102 | 94 57 25 103 | 95 57 44 104 | 96 61 45 105 | 97 61 47 106 | 98 63 6 107 | 99 64 22 108 | 100 71 11 109 | 101 71 13 110 | 102 71 16 111 | 103 71 45 112 | 104 71 47 113 | 105 74 12 114 | 106 74 16 115 | 107 74 20 116 | 108 74 24 117 | 109 74 29 118 | 110 74 35 119 | 111 74 39 120 | 112 74 6 121 | 113 77 21 122 | 114 78 10 123 | 115 78 32 124 | 116 78 35 125 | 117 78 39 126 | 118 79 10 127 | 119 79 33 128 | 120 79 37 129 | 121 80 10 130 | 122 80 41 131 | 123 80 5 132 | 124 81 17 133 | 125 84 20 134 | 126 84 24 135 | 127 84 29 136 | 128 84 34 137 | 129 84 38 138 | 130 84 6 139 | 131 107 27 140 | EOF 141 | -------------------------------------------------------------------------------- /GA_tsp_optimisation/crossover.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | class Crossover: 5 | def __init__(self, crossover_type, **kwargs): 6 | self.crossover_type = crossover_type 7 | 8 | def crossover(self, parent_1, parent_2, **kwargs): 9 | if self.crossover_type == 'pmx': 10 | return self.crossover_pmx(parent_1=parent_1, parent_2=parent_2) 11 | if self.crossover_type == 'ordered': 12 | return self.ordered_crossover(parent_1=parent_1, parent_2=parent_2) 13 | if self.crossover_type == 'cycle': 14 | return self.cycle_crossover(parent_1=parent_1, parent_2=parent_2) 15 | 16 | def crossover_pmx(self, parent_1, parent_2): 17 | points_num = len(parent_1) 18 | cut_ix = np.random.choice(points_num - 2, 2, replace=False) 19 | min_ix = np.min(cut_ix) 20 | max_ix = np.max(cut_ix) 21 | offspring_1 = np.zeros(points_num) 22 | 23 | 24 | def ordered_crossover(self, parent_1, parent_2): 25 | points_num = len(parent_1) 26 | cut_ix = np.random.choice(points_num - 2, 2, replace=False) 27 | min_ix = np.min(cut_ix) 28 | max_ix = np.max(cut_ix) 29 | offspring_1 = np.zeros(points_num) 30 | current_ix = 0 31 | set_1 = parent_1[min_ix:max_ix] 32 | for i, elem in enumerate(parent_2): 33 | if elem not in set_1: 34 | if current_ix != min_ix: 35 | offspring_1[current_ix] = elem 36 | else: 37 | current_ix = max_ix 38 | offspring_1[current_ix] = elem 39 | current_ix += 1 40 | offspring_1[min_ix:max_ix] = set_1 41 | offspring_2 = np.zeros(points_num) 42 | current_ix = 0 43 | set_2 = parent_2[min_ix:max_ix] 44 | for i, elem in enumerate(parent_1): 45 | if elem not in set_2: 46 | if current_ix != min_ix: 47 | offspring_2[current_ix] = elem 48 | else: 49 | current_ix = max_ix 50 | offspring_2[current_ix] = elem 51 | current_ix += 1 52 | offspring_2[min_ix:max_ix] = set_2 53 | return [int(i) for i in offspring_1], [int(i) for i in offspring_2] 54 | 55 | def cycle_crossover(self, parent_1, parent_2): 56 | raise NotImplementedError 57 | -------------------------------------------------------------------------------- /GA_tsp_optimisation/ga_pipeline.py: -------------------------------------------------------------------------------- 1 | from data_utils import * 2 | import operator 3 | from GA_tsp_optimisation import Selector, Crossover, Mutation 4 | from vis import * 5 | from data_utils import create_matrix 6 | import random 7 | 8 | coordinates = None 9 | matrix = None 10 | 11 | 12 | class Path: 13 | def __init__(self, path): 14 | self.path = path 15 | self.fitness = _evaluate_fitness(path) 16 | self._prob = None 17 | 18 | def update_path(self, new_path): 19 | self.path = new_path 20 | self.fitness = _evaluate_fitness(new_path) 21 | 22 | 23 | def _evaluate_fitness(path): 24 | dist = 0 25 | for i in range(len(path) - 1): 26 | if i == (len(path) - 1): 27 | dist += matrix[path[0]][path[i + 1]] 28 | break 29 | dist += matrix[path[i + 1]][path[i]] 30 | return dist 31 | 32 | 33 | def _generate_population(num_of_cities, population_size): 34 | population = [] 35 | for _ in range(population_size): 36 | path = np.random.permutation([i for i in range(num_of_cities)]) 37 | population.append(Path(path)) 38 | # draw_path(path, coordinates) 39 | return population 40 | 41 | 42 | def ga_pipeline(mat=None, population_size=20, generations=200, best_perc=0.2, 43 | mutation_probability=0.2, mutation_intensity=0.3, 44 | verbose=1, coord=None, plot=0): 45 | num_of_cities = mat.shape[0] 46 | global matrix 47 | matrix = mat 48 | global coordinates 49 | coordinates = coord 50 | population = _generate_population(num_of_cities, population_size) 51 | s = Selector(selection_type='roulette') 52 | c = Crossover(crossover_type='ordered') 53 | m = Mutation(mutation_type='rsm') 54 | x, y = [], [] 55 | for ii in range(generations): 56 | population.sort(key=operator.attrgetter('fitness'), reverse=False) 57 | new_generation = [] 58 | for i in range(int(population_size * best_perc)): 59 | new_generation.append(population[i]) 60 | pairs_generator = s.selection(population=population, best_perc=best_perc) 61 | for i, j in pairs_generator: 62 | child_1, child_2 = c.crossover(parent_1=i.path, parent_2=j.path) 63 | new_generation.append(Path(child_1)) 64 | new_generation.append(Path(child_2)) 65 | population = new_generation[:population_size] 66 | for i in range(1, len(population)): 67 | population[i].update_path(m.mutation(population[i].path, mutation_probability=mutation_probability)) 68 | population.sort(key=operator.attrgetter('fitness'), reverse=False) 69 | if verbose: 70 | print('========== generation %s ==========' % ii) 71 | print('best so far: %s\n' % population[0].fitness) 72 | x.append(ii) 73 | y.append(population[0].fitness) 74 | if plot: 75 | if ii % 500 == 0: 76 | draw_path(population[0].path, coordinates, ii) 77 | draw_convergence(x, y, 'ps = %s, bp = %s, mr = %s, mi = %s' % ( 78 | round(population_size, 2), round(best_perc, 2), round(mutation_probability, 2), round(mutation_intensity, 2))) 79 | return population[0].fitness 80 | -------------------------------------------------------------------------------- /firefly_optimisation/benchmark_functions_.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 3 | set of benchmark functions for global firefly_optimisation 4 | 5 | ''' 6 | 7 | import numpy as np 8 | import matplotlib.pyplot as plt 9 | from mpl_toolkits.mplot3d import Axes3D 10 | 11 | IMG_DIR = 'figures/' 12 | 13 | __all__ = ['Ackley', 'Michalewicz'] 14 | 15 | 16 | class BaseFunc: 17 | def __init__(self, dim): 18 | self.dim = dim 19 | self.min_bound = np.zeros(self.dim) 20 | self.max_bound = np.zeros(self.dim) 21 | self.solution = np.zeros(self.dim) 22 | self.global_optima = 0 23 | self.plot_place = 0.25 24 | self.m = 10 25 | self.title = '' 26 | 27 | def get_global_optima(self): 28 | return self.global_optima 29 | 30 | def get_solution(self): 31 | return self.solution 32 | 33 | def get_search_bounds(self): 34 | return [self.min_bound, self.max_bound] 35 | 36 | def get_y(self, x): 37 | return -1 38 | 39 | def plot(self): 40 | x = np.arange(self.min_bound[0], self.max_bound[0], self.plot_place, dtype=np.float32) 41 | y = np.arange(self.min_bound[1], self.max_bound[1], self.plot_place, dtype=np.float32) 42 | X, Y = np.meshgrid(x, y) 43 | Z = [] 44 | for coord in zip(X, Y): 45 | z = [] 46 | for input in zip(coord[0], coord[1]): 47 | tmp = list(input) 48 | tmp.extend(list(self.solution[0:self.dim - 2])) 49 | z.append(self.get_y(np.array(tmp))) 50 | Z.append(z) 51 | Z = np.array(Z) 52 | fig = plt.figure() 53 | ax = Axes3D(fig) 54 | ax.plot_wireframe(X, Y, Z) 55 | plt.show() 56 | 57 | 58 | class Ackley(BaseFunc): 59 | 60 | def __init__(self, dim): 61 | super().__init__(dim) 62 | self.max_bound = np.array([32.768] * self.dim) 63 | self.min_bound = np.array([-32.768] * self.dim) 64 | self.solution = np.ones(self.dim) 65 | self.global_optima = 0 66 | self.title = 'Ackley' 67 | 68 | def get_y(self, x): 69 | return 20. - 20. * np.exp(-0.2 * np.sqrt(1. / self.dim * np.sum(np.square(x)))) + np.e - np.exp( 70 | 1. / self.dim * np.sum(np.cos(x * 2. * np.pi))) 71 | 72 | def get_y_2d(self, x, y): 73 | return 20. - 20. * np.exp(-0.2 * np.sqrt(1. / self.dim * (x ** 2 + y ** 2))) + np.e - np.exp( 74 | 1. / self.dim * (np.cos(x * 2. * np.pi) + np.cos(y * 2. * np.pi))) 75 | 76 | 77 | class Michalewicz(BaseFunc): 78 | 79 | def __init__(self, dim): 80 | super().__init__(dim) 81 | self.max_bound = np.array([np.pi] * self.dim) 82 | self.min_bound = np.zeros(self.dim) 83 | self.solution = np.zeros(self.dim) 84 | self.global_optima = self.get_y(self.solution) 85 | self.title = 'Michalewicz' 86 | self.m = 10 87 | 88 | def get_y(self, x): 89 | y = 0 90 | for i in range(self.dim): 91 | y += np.sin(x[i]) * np.power(np.sin((i + 1) * np.power(x[i], 2) / np.pi), 2 * self.m) 92 | return -y 93 | 94 | def get_y_2d(self, x, y): 95 | yy = 0 96 | yy += np.sin(x) * np.power(np.sin((0 + 1) * np.power(x, 2) / np.pi), 2 * self.m) 97 | yy += np.sin(y) * np.power(np.sin((1 + 1) * np.power(y, 2) / np.pi), 2 * self.m) 98 | return -yy 99 | -------------------------------------------------------------------------------- /firefly_optimisation/firefly_.py: -------------------------------------------------------------------------------- 1 | from data_utils import generate_population 2 | from firefly_optimisation import Ackley, Michalewicz 3 | import numpy as np 4 | import math 5 | import operator 6 | 7 | 8 | # TODO: refactor function firefly_optimisation function call 9 | class Firefly: 10 | 11 | def __init__(self, problem_dim, min_bound, max_bound): 12 | self.func = Michalewicz(problem_dim) 13 | self.position = generate_population(1, problem_dim, min_bound, max_bound)[0] 14 | self.brightness = None 15 | self.update_brightness() 16 | 17 | # the best fit is 0 18 | def update_brightness(self): 19 | self.brightness = -self.func.get_y(self.position) 20 | 21 | 22 | class FireflyOptimizer: 23 | 24 | def __init__(self, **kwargs): 25 | self.population_size = int(kwargs.get('population_size', 10)) 26 | self.problem_dim = kwargs.get('problem_dim', 2) 27 | self.min_bound = kwargs.get('min_bound', -5) 28 | self.max_bound = kwargs.get('max_bound', 5) 29 | self.generations = kwargs.get('generations', 10) 30 | self.population = self._population(self.population_size, self.problem_dim, self.min_bound, self.max_bound) 31 | self.gamma = kwargs.get('gamma', 0.97) # absorption coefficient 32 | self.alpha = kwargs.get('alpha', 0.25) # randomness [0,1] 33 | self.beta_init = kwargs.get('beta_init', 1) 34 | self.beta_min = kwargs.get('beta_min', 0.2) 35 | self.optimization_benchmark = kwargs.get('optimization_benchmark', 'Ackley') 36 | 37 | @staticmethod 38 | def _population(population_size, problem_dim, min_bound, max_bound): 39 | population = [] 40 | for i in range(population_size): 41 | population.append(Firefly(problem_dim, min_bound, max_bound)) 42 | return population 43 | 44 | def step(self): 45 | self.population.sort(key=operator.attrgetter('brightness'), reverse=True) 46 | self._modify_alpha() 47 | tmp_population = self.population 48 | for i in range(self.population_size): 49 | for j in range(self.population_size): 50 | if self.population[i].brightness > tmp_population[j].brightness: 51 | r = math.sqrt(np.sum((self.population[i].position - tmp_population[j].position) ** 2)) 52 | beta = (self.beta_init - self.beta_min) * math.exp(-self.gamma * r ** 2) + self.beta_min 53 | tmp = self.alpha * (np.random.random_sample((1, self.problem_dim))[0] - 0.5) * ( 54 | self.max_bound - self.min_bound) 55 | self.population[j].position = self.check_position( 56 | self.population[i].position * (1 - beta) + tmp_population[ 57 | j].position * beta + tmp) 58 | self.population[j].update_brightness() 59 | self.population[0].position = generate_population(1, self.problem_dim, self.min_bound, self.max_bound)[0] 60 | self.population[0].update_brightness() 61 | 62 | def run_firefly(self): 63 | for t in range(self.generations): 64 | print('Generation %s, best fitness %s' % (t, self.population[0].brightness)) 65 | self.step() 66 | self.population.sort(key=operator.attrgetter('brightness'), reverse=True) 67 | return self.population[0].brightness, self.population[0].position 68 | 69 | def check_position(self, position): 70 | position[position > self.max_bound] = self.max_bound 71 | position[position < self.min_bound] = self.min_bound 72 | return position 73 | 74 | def _modify_alpha(self): 75 | delta = 1 - (10 ** (-4) / 0.9) ** (1 / self.generations) 76 | self.alpha = (1 - delta) * self.alpha 77 | -------------------------------------------------------------------------------- /data_utils/data/xqg237.tsp: -------------------------------------------------------------------------------- 1 | NAME : xqg237 2 | COMMENT : Bonn VLSI data set with 237 points 3 | COMMENT : Uni Bonn, Research Institute for Discrete Math 4 | COMMENT : Contributed by Andre Rohe 5 | TYPE : TSP 6 | DIMENSION : 237 7 | EDGE_WEIGHT_TYPE : EUC_2D 8 | NODE_COORD_SECTION 9 | 1 0 15 10 | 2 0 33 11 | 3 0 51 12 | 4 0 60 13 | 5 2 21 14 | 6 2 28 15 | 7 2 44 16 | 8 5 13 17 | 9 5 18 18 | 10 5 33 19 | 11 5 36 20 | 12 5 40 21 | 13 5 64 22 | 14 5 8 23 | 15 9 10 24 | 16 10 10 25 | 17 10 42 26 | 18 11 10 27 | 19 11 17 28 | 20 11 31 29 | 21 11 34 30 | 22 11 42 31 | 23 12 10 32 | 24 12 5 33 | 25 12 61 34 | 26 15 13 35 | 27 15 18 36 | 28 15 64 37 | 29 15 8 38 | 30 17 21 39 | 31 18 11 40 | 32 18 13 41 | 33 18 14 42 | 34 18 15 43 | 35 18 28 44 | 36 25 10 45 | 37 25 12 46 | 38 25 14 47 | 39 25 16 48 | 40 25 18 49 | 41 25 20 50 | 42 25 23 51 | 43 25 32 52 | 44 25 34 53 | 45 25 45 54 | 46 25 61 55 | 47 28 12 56 | 48 28 15 57 | 49 28 19 58 | 50 28 23 59 | 51 28 27 60 | 52 28 37 61 | 53 28 40 62 | 54 28 42 63 | 55 28 7 64 | 56 32 10 65 | 57 32 13 66 | 58 33 10 67 | 59 33 13 68 | 60 34 10 69 | 61 34 13 70 | 62 34 28 71 | 63 34 36 72 | 64 34 39 73 | 65 34 5 74 | 66 34 51 75 | 67 35 24 76 | 68 38 27 77 | 69 38 37 78 | 70 38 40 79 | 71 38 42 80 | 72 40 45 81 | 73 41 61 82 | 74 48 10 83 | 75 48 32 84 | 76 48 34 85 | 77 48 45 86 | 78 48 61 87 | 79 51 11 88 | 80 51 14 89 | 81 51 18 90 | 82 51 22 91 | 83 51 26 92 | 84 51 37 93 | 85 51 40 94 | 86 51 42 95 | 87 51 7 96 | 88 54 0 97 | 89 55 0 98 | 90 55 19 99 | 91 55 22 100 | 92 55 26 101 | 93 56 10 102 | 94 56 20 103 | 95 56 24 104 | 96 57 10 105 | 97 57 12 106 | 98 57 28 107 | 99 57 36 108 | 100 57 39 109 | 101 57 5 110 | 102 57 51 111 | 103 58 15 112 | 104 61 11 113 | 105 61 18 114 | 106 61 21 115 | 107 61 25 116 | 108 61 37 117 | 109 61 40 118 | 110 61 42 119 | 111 63 45 120 | 112 64 61 121 | 113 71 54 122 | 114 71 56 123 | 115 71 6 124 | 116 72 0 125 | 117 73 0 126 | 118 74 13 127 | 119 74 19 128 | 120 74 25 129 | 121 74 31 130 | 122 74 37 131 | 123 74 43 132 | 124 74 49 133 | 125 74 59 134 | 126 74 62 135 | 127 74 64 136 | 128 78 10 137 | 129 79 10 138 | 130 80 10 139 | 131 80 47 140 | 132 80 50 141 | 133 80 58 142 | 134 80 61 143 | 135 81 10 144 | 136 84 13 145 | 137 84 19 146 | 138 84 25 147 | 139 84 31 148 | 140 84 37 149 | 141 84 43 150 | 142 84 59 151 | 143 84 62 152 | 144 84 64 153 | 145 86 6 154 | 146 87 11 155 | 147 87 13 156 | 148 87 15 157 | 149 87 17 158 | 150 87 19 159 | 151 87 21 160 | 152 87 23 161 | 153 87 25 162 | 154 87 27 163 | 155 87 29 164 | 156 87 31 165 | 157 87 33 166 | 158 87 35 167 | 159 87 37 168 | 160 87 39 169 | 161 87 41 170 | 162 87 42 171 | 163 87 44 172 | 164 87 45 173 | 165 94 10 174 | 166 94 20 175 | 167 94 22 176 | 168 94 34 177 | 169 94 35 178 | 170 94 36 179 | 171 94 52 180 | 172 94 6 181 | 173 97 11 182 | 174 97 15 183 | 175 97 25 184 | 176 97 28 185 | 177 97 30 186 | 178 97 57 187 | 179 97 60 188 | 180 97 64 189 | 181 102 10 190 | 182 103 10 191 | 183 103 16 192 | 184 103 24 193 | 185 103 27 194 | 186 103 42 195 | 187 103 55 196 | 188 103 58 197 | 189 104 12 198 | 190 107 11 199 | 191 107 15 200 | 192 107 25 201 | 193 107 28 202 | 194 107 30 203 | 195 109 34 204 | 196 109 35 205 | 197 109 36 206 | 198 109 6 207 | 199 110 52 208 | 200 117 50 209 | 201 117 52 210 | 202 117 63 211 | 203 120 11 212 | 204 120 15 213 | 205 120 19 214 | 206 120 25 215 | 207 120 29 216 | 208 120 34 217 | 209 120 40 218 | 210 120 44 219 | 211 120 55 220 | 212 120 58 221 | 213 120 60 222 | 214 120 7 223 | 215 123 26 224 | 216 124 37 225 | 217 124 40 226 | 218 124 44 227 | 219 125 38 228 | 220 125 42 229 | 221 126 46 230 | 222 126 5 231 | 223 126 54 232 | 224 126 57 233 | 225 127 12 234 | 226 127 16 235 | 227 130 15 236 | 228 130 19 237 | 229 130 25 238 | 230 130 29 239 | 231 130 34 240 | 232 130 39 241 | 233 130 43 242 | 234 130 55 243 | 235 130 58 244 | 236 130 60 245 | 237 132 63 246 | EOF 247 | -------------------------------------------------------------------------------- /data_utils/data/pma343.tsp: -------------------------------------------------------------------------------- 1 | NAME : pma343 2 | COMMENT : Bonn VLSI data set with 343 points 3 | COMMENT : Uni Bonn, Research Institute for Discrete Math 4 | COMMENT : Contributed by Andre Rohe 5 | TYPE : TSP 6 | DIMENSION : 343 7 | EDGE_WEIGHT_TYPE : EUC_2D 8 | NODE_COORD_SECTION 9 | 1 0 13 10 | 2 0 16 11 | 3 0 19 12 | 4 0 22 13 | 5 0 25 14 | 6 0 28 15 | 7 0 31 16 | 8 0 34 17 | 9 0 37 18 | 10 0 4 19 | 11 0 40 20 | 12 0 7 21 | 13 2 10 22 | 14 2 12 23 | 15 2 14 24 | 16 2 16 25 | 17 2 21 26 | 18 2 23 27 | 19 2 25 28 | 20 2 27 29 | 21 2 34 30 | 22 5 20 31 | 23 5 8 32 | 24 6 42 33 | 25 8 17 34 | 26 8 28 35 | 27 9 10 36 | 28 9 21 37 | 29 11 18 38 | 30 12 17 39 | 31 12 28 40 | 32 12 5 41 | 33 15 17 42 | 34 15 28 43 | 35 15 8 44 | 36 18 34 45 | 37 25 10 46 | 38 25 12 47 | 39 25 14 48 | 40 25 16 49 | 41 25 25 50 | 42 25 27 51 | 43 25 29 52 | 44 25 31 53 | 45 25 6 54 | 46 28 19 55 | 47 28 21 56 | 48 28 34 57 | 49 31 17 58 | 50 31 32 59 | 51 32 10 60 | 52 32 25 61 | 53 34 18 62 | 54 34 33 63 | 55 35 17 64 | 56 35 32 65 | 57 38 17 66 | 58 38 19 67 | 59 38 21 68 | 60 38 32 69 | 61 38 34 70 | 62 40 6 71 | 63 48 10 72 | 64 48 12 73 | 65 48 13 74 | 66 48 14 75 | 67 48 21 76 | 68 48 23 77 | 69 48 24 78 | 70 48 25 79 | 71 48 28 80 | 72 48 35 81 | 73 51 18 82 | 74 51 8 83 | 75 55 12 84 | 76 55 23 85 | 77 57 11 86 | 78 57 16 87 | 79 57 22 88 | 80 58 5 89 | 81 61 14 90 | 82 61 25 91 | 83 61 8 92 | 84 63 28 93 | 85 64 35 94 | 86 71 15 95 | 87 71 17 96 | 88 71 18 97 | 89 71 19 98 | 90 71 21 99 | 91 71 23 100 | 92 71 24 101 | 93 71 25 102 | 94 71 34 103 | 95 71 36 104 | 96 74 11 105 | 97 74 29 106 | 98 74 6 107 | 99 74 9 108 | 100 78 17 109 | 101 78 21 110 | 102 80 16 111 | 103 80 23 112 | 104 80 27 113 | 105 80 30 114 | 106 80 5 115 | 107 80 8 116 | 108 84 11 117 | 109 84 19 118 | 110 84 6 119 | 111 84 9 120 | 112 93 0 121 | 113 94 10 122 | 114 94 14 123 | 115 94 16 124 | 116 94 18 125 | 117 94 20 126 | 118 94 23 127 | 119 94 25 128 | 120 94 27 129 | 121 94 29 130 | 122 94 31 131 | 123 97 34 132 | 124 97 36 133 | 125 100 21 134 | 126 100 32 135 | 127 101 14 136 | 128 101 25 137 | 129 103 33 138 | 130 104 21 139 | 131 104 32 140 | 132 107 21 141 | 133 107 32 142 | 134 107 34 143 | 135 107 36 144 | 136 109 23 145 | 137 110 10 146 | 138 117 10 147 | 139 117 12 148 | 140 117 13 149 | 141 117 14 150 | 142 117 33 151 | 143 117 35 152 | 144 120 19 153 | 145 120 23 154 | 146 120 27 155 | 147 120 6 156 | 148 124 12 157 | 149 124 20 158 | 150 124 23 159 | 151 124 27 160 | 152 125 21 161 | 153 125 25 162 | 154 126 11 163 | 155 126 29 164 | 156 126 5 165 | 157 127 16 166 | 158 130 14 167 | 159 130 19 168 | 160 130 22 169 | 161 130 26 170 | 162 130 6 171 | 163 140 20 172 | 164 140 22 173 | 165 140 24 174 | 166 140 26 175 | 167 140 28 176 | 168 140 6 177 | 169 143 10 178 | 170 143 13 179 | 171 143 17 180 | 172 143 21 181 | 173 143 31 182 | 174 143 34 183 | 175 143 36 184 | 176 146 29 185 | 177 147 22 186 | 178 148 20 187 | 179 149 11 188 | 180 149 20 189 | 181 149 30 190 | 182 149 33 191 | 183 149 42 192 | 184 149 8 193 | 185 150 29 194 | 186 153 21 195 | 187 153 29 196 | 188 153 31 197 | 189 153 34 198 | 190 153 36 199 | 191 155 6 200 | 192 163 10 201 | 193 163 12 202 | 194 163 14 203 | 195 163 16 204 | 196 163 18 205 | 197 163 20 206 | 198 163 22 207 | 199 163 24 208 | 200 166 28 209 | 201 166 31 210 | 202 166 35 211 | 203 166 8 212 | 204 169 17 213 | 205 169 25 214 | 206 170 10 215 | 207 170 18 216 | 208 172 26 217 | 209 172 29 218 | 210 173 17 219 | 211 173 25 220 | 212 173 5 221 | 213 176 17 222 | 214 176 25 223 | 215 176 8 224 | 216 186 10 225 | 217 186 14 226 | 218 186 16 227 | 219 186 18 228 | 220 186 20 229 | 221 186 26 230 | 222 189 23 231 | 223 195 14 232 | 224 195 21 233 | 225 195 22 234 | 226 195 32 235 | 227 199 21 236 | 228 199 23 237 | 229 201 26 238 | 230 202 10 239 | 231 209 10 240 | 232 209 11 241 | 233 209 12 242 | 234 209 15 243 | 235 209 17 244 | 236 209 19 245 | 237 209 21 246 | 238 209 23 247 | 239 209 25 248 | 240 209 27 249 | 241 209 29 250 | 242 209 32 251 | 243 209 34 252 | 244 209 37 253 | 245 212 16 254 | 246 212 20 255 | 247 212 24 256 | 248 212 28 257 | 249 212 33 258 | 250 212 37 259 | 251 212 8 260 | 252 216 14 261 | 253 216 31 262 | 254 217 14 263 | 255 217 31 264 | 256 218 14 265 | 257 218 31 266 | 258 219 5 267 | 259 222 8 268 | 260 224 11 269 | 261 224 12 270 | 262 232 13 271 | 263 232 21 272 | 264 232 23 273 | 265 232 25 274 | 266 232 27 275 | 267 232 29 276 | 268 232 31 277 | 269 232 6 278 | 270 235 18 279 | 271 235 22 280 | 272 235 26 281 | 273 235 30 282 | 274 235 34 283 | 275 235 36 284 | 276 239 20 285 | 277 240 20 286 | 278 241 16 287 | 279 241 20 288 | 280 241 33 289 | 281 245 34 290 | 282 245 36 291 | 283 247 6 292 | 284 248 13 293 | 285 255 10 294 | 286 255 12 295 | 287 255 13 296 | 288 255 15 297 | 289 255 31 298 | 290 255 32 299 | 291 255 34 300 | 292 255 36 301 | 293 258 12 302 | 294 258 14 303 | 295 258 16 304 | 296 258 20 305 | 297 258 24 306 | 298 258 28 307 | 299 258 6 308 | 300 262 10 309 | 301 262 21 310 | 302 262 24 311 | 303 262 28 312 | 304 263 10 313 | 305 263 13 314 | 306 263 15 315 | 307 263 22 316 | 308 263 26 317 | 309 264 10 318 | 310 264 13 319 | 311 264 15 320 | 312 264 30 321 | 313 264 5 322 | 314 265 17 323 | 315 268 14 324 | 316 268 16 325 | 317 268 20 326 | 318 268 23 327 | 319 268 27 328 | 320 268 6 329 | 321 278 10 330 | 322 278 12 331 | 323 278 13 332 | 324 278 19 333 | 325 278 35 334 | 326 281 12 335 | 327 281 14 336 | 328 281 16 337 | 329 281 7 338 | 330 285 10 339 | 331 286 10 340 | 332 286 13 341 | 333 287 10 342 | 334 287 13 343 | 335 287 15 344 | 336 287 25 345 | 337 287 5 346 | 338 291 14 347 | 339 291 16 348 | 340 293 19 349 | 341 294 35 350 | 342 299 16 351 | 343 299 21 352 | EOF 353 | --------------------------------------------------------------------------------