{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"Lab 9.2 Εισαγωγή στην βιβλοθήκη ΓΑ DEAP.ipynb","provenance":[]},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"cell_type":"markdown","metadata":{"collapsed":true,"id":"FXNCweHxH2bF"},"source":[""]},{"cell_type":"markdown","metadata":{"id":"qZ-8g6AuH2bI"},"source":["Στην Python είναι διαθέσιμες πολλές βιβλιοθήκες γενετικών αλγόριθμων: \n","- [Pyevolve](http://pyevolve.sourceforge.net/)\n","- [pyeasyga](https://pypi.python.org/pypi/pyeasyga)\n","- [PyOCL/OpenCLGA](https://github.com/PyOCL/OpenCLGA)\n","- [inspyred](https://pypi.python.org/pypi/inspyred)\n","\n","Θα βρούμε επίσης γενετικούς σε βιβλιοθήκες βελτιστοποίησης όπως η [PyOpt](http://www.pyopt.org/)\n","\n","Μια από τις πιο ενεργές, ευρύτερα χρησιμοποιούμενες και ενδιαφέρουσες βιβλιοθήκες είναι η [DEAP (Distributed Evolutionary Algorithms in Python)](https://github.com/DEAP/deap). Τα κύρια πλεονεκτήματα της DEAP είναι:\n","- μπορεί να αναπτύξει κανείς γενετικούς αλγόριθμους πάνω σε οποιαδήποτε δομή δεδομένων (list, array, set, dictionary, tree, numpy array κλπ) ή ad hoc κλάση. \n","- είναι διαθέσιμοι πολλοί γνωστοί γενετικοί τελεστές και ολόκληροι αλγόριθμοι, αλλά έχει κανείς επίσης τη δυνατότητα να ορίζει εύκολα τους δικούς του, κάτι που είναι ιδιάιτερα χρήσιμο στις περιπτώσεις που είναι ιδιαίτερα εξαρτώμενοι από το πρόβλημα.\n","- οι γενετικοί είναι από τη φύση τους πολύ κατάλληλοι για παράλληλη εκτέλεση (παράδειγμα: η αποτίμηση της καταλληλότητας ενός πληθυσμού). Η DEAP σε συνδυασμό με τις βιβλιοθήκες \"SCOOP\" και \"multiprocessing\" προσφέρει ένα πολύ εύκολο τρόπο για αποτελεσματική παράλληλη υλοποίηση των αλγόριθμων.\n","\n","# Εισαγωγή\n","\n","Εγκατάσταση:"]},{"cell_type":"code","metadata":{"scrolled":true,"id":"lFfKCr9jH2bJ","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1638783336216,"user_tz":-120,"elapsed":4161,"user":{"displayName":"Giorgos Siolas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjxnZOAObbc3X0z9X2rs1N_1geznqhrotkq3KF-p_M=s64","userId":"10127542075805046236"}},"outputId":"51e487b9-5cfb-458f-8a6e-6fdabc1c55da"},"source":["! pip install -U deap"],"execution_count":1,"outputs":[{"output_type":"stream","name":"stdout","text":["Collecting deap\n"," Downloading deap-1.3.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (160 kB)\n","\u001b[?25l\r\u001b[K |██ | 10 kB 18.6 MB/s eta 0:00:01\r\u001b[K |████ | 20 kB 22.5 MB/s eta 0:00:01\r\u001b[K |██████ | 30 kB 26.8 MB/s eta 0:00:01\r\u001b[K |████████▏ | 40 kB 28.0 MB/s eta 0:00:01\r\u001b[K |██████████▏ | 51 kB 28.9 MB/s eta 0:00:01\r\u001b[K |████████████▏ | 61 kB 27.3 MB/s eta 0:00:01\r\u001b[K |██████████████▎ | 71 kB 27.7 MB/s eta 0:00:01\r\u001b[K |████████████████▎ | 81 kB 28.0 MB/s eta 0:00:01\r\u001b[K |██████████████████▎ | 92 kB 29.6 MB/s eta 0:00:01\r\u001b[K |████████████████████▍ | 102 kB 31.2 MB/s eta 0:00:01\r\u001b[K |██████████████████████▍ | 112 kB 31.2 MB/s eta 0:00:01\r\u001b[K |████████████████████████▍ | 122 kB 31.2 MB/s eta 0:00:01\r\u001b[K |██████████████████████████▌ | 133 kB 31.2 MB/s eta 0:00:01\r\u001b[K |████████████████████████████▌ | 143 kB 31.2 MB/s eta 0:00:01\r\u001b[K |██████████████████████████████▌ | 153 kB 31.2 MB/s eta 0:00:01\r\u001b[K |████████████████████████████████| 160 kB 31.2 MB/s \n","\u001b[?25hRequirement already satisfied: numpy in /usr/local/lib/python3.7/dist-packages (from deap) (1.19.5)\n","Installing collected packages: deap\n","Successfully installed deap-1.3.1\n"]}]},{"cell_type":"markdown","metadata":{"id":"lZp0KD2lH2bT"},"source":["## Βασικές έννοιες: Creator, Base, Fitness\n","\n","Οι δύο βασικές έννοιες του DEAP είναι οι Creator και Base. \n","\n","O **Creator** είναι ένα μέτα - εργοστάσιο δημιουργίας κλάσεων που θα χρησιμεύσουν στον γενετικό αλγόριθμο. Η **Base** είναι ένα δομοστοιχείο που παρέχει δύο βασικές δομές (κλάσεις) για την κατασκευή του γενετικού: το **Toolbox**, που θα χρησιμοποιήσουμε για να αποθηκέυσουμε (εισάγουμε) τους τελεστές και την (εικονική) κλάση **Fitness** που θα χρησιμοποιήσουμε για να κατασκευάσουμε το μέλος καταλληλότητας του κάθε ατόμου.\n","\n","Ας πούμε ότι θέλουμε να ορίσουμε μια συνάρτηση καταλληλότητας προς *ελαχιστοποίηση*:"]},{"cell_type":"code","metadata":{"id":"1LzceeQWH2bU","executionInfo":{"status":"ok","timestamp":1638783484572,"user_tz":-120,"elapsed":763,"user":{"displayName":"Giorgos Siolas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjxnZOAObbc3X0z9X2rs1N_1geznqhrotkq3KF-p_M=s64","userId":"10127542075805046236"}}},"source":["from deap import base, creator\n","creator.create(\"FitnessMin\", base.Fitness, weights=(-1.0,))"],"execution_count":2,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"JeSOhuhnH2bY"},"source":["Η συνάρτηση `create` λαμβάνει τουλάχιστον __δύο ορίσματα__: το όνομα της κλάσης που θα δημιουργήσουμε και τη βασική κλάση από την οποία θα κληρονομίσει. Τα ακόλουθα ορίσματα αποτελούν χαρακτηριστικά της κλάσης. Στην περίπτωση του παραδείγματος, η νέα κλάση `FitnessMin` κληρονομεί την `base.Fitness` με χαρακτηριστικό την πλειάδα weights (-1.0,). Το (-1.0,) σημαίνει ότι θέλουμε να ελαχιστοποιήσουμε ένα μόνο κριτήριο. Εξ ορισμού το DEAP είναι σχεδιασμένο για πολυ-κριτηριακή βελτιστοποίηση (multi-objective optimization) και γι' αυτό αναμένει μια πλειάδα βαρών εξού και το \",\". Αν θέλαμε να μεγιστοποιήσουμε ένα κριτήριο θα θέταμε weights=(1.0,). Στη μονοκριτηριακή βελτιστοποίηση σημασία έχει μόνο το πρόσημο του βάρους. Για πολυκριτηριακή βελτιστοποίηση τα βάρη καθορίζουν τη σχετική σημασία των κριτηρίων. Για παράδειγμα το weights=(-1.0,2.0) ορίζει μια πολυκριτηριακή βελτιστοποίηση όπου θέλουμε να ελαχιστοποιήσουμε το πρώτο κριτήριο, να μεγιστοποιήσουμε το δεύτερο, και το δεύτερο έχει διπλάσια σημασία (βάρος) από το πρώτο.\n","\n","Στη συνέχεια ορίζουμε την κλάση του **ατόμου** το οποίο κληρονομεί από τον τύπο *list* και περιλαμβάνει το χαρακτηρηστικό `FitnessMin`"]},{"cell_type":"code","metadata":{"id":"A3ykofgnH2ba","executionInfo":{"status":"ok","timestamp":1638783511214,"user_tz":-120,"elapsed":249,"user":{"displayName":"Giorgos Siolas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjxnZOAObbc3X0z9X2rs1N_1geznqhrotkq3KF-p_M=s64","userId":"10127542075805046236"}}},"source":["creator.create(\"Individual\", list, fitness=creator.FitnessMin)"],"execution_count":3,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"zOANJHquH2bg"},"source":["Μπορούμε τώρα να ορίσουμε ένα στιγμιότυπο ατόμου και να υπολογίσουμε/ορίσουμε την καταλληλότητα του:"]},{"cell_type":"code","metadata":{"id":"aqOL19WbH2bh","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1638783578711,"user_tz":-120,"elapsed":289,"user":{"displayName":"Giorgos Siolas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjxnZOAObbc3X0z9X2rs1N_1geznqhrotkq3KF-p_M=s64","userId":"10127542075805046236"}},"outputId":"48c1484d-8f7d-4eea-8197-1d5eb0412724"},"source":["ind = creator.Individual([1,0,1,0,1])\n","ind.fitness.values = (sum(ind),)\n","\n","print(ind)\n","print(type(ind))\n","print(type(ind.fitness))\n","print(ind.fitness.values)"],"execution_count":4,"outputs":[{"output_type":"stream","name":"stdout","text":["[1, 0, 1, 0, 1]\n","\n","\n","(3.0,)\n"]}]},{"cell_type":"markdown","metadata":{"id":"dbzqe-O7H2bn"},"source":["Στο συγκεκριμένο παράδειγμα ορίσαμε ως καταλληλότητα το άθροισμα των στοιχείων της λίστας που αποτελούν το άτομο ind. Η καταλληλότητα στο DEAP είναι πάντα πλειάδα και η μονοκριτηριακή βελτιστοποίηση είναι μια ειδική περίπτωση (προσέξτε το \",\"). Επίσης προσέξτε ότι εμείς ορίζουμε τις τιμές του χαρακτηρηστικού fitness.values. \n","\n","## Τελεστές\n","\n","Το DEAP μας επιτρέπει να δημιουργούμε τελεστές μαζί με τις παραμέτρους τους και να τους ομαδοποιούμε σε εργαλιοθήκες (toolbox):"]},{"cell_type":"code","metadata":{"id":"LKKLdIs1H2br","executionInfo":{"status":"ok","timestamp":1638783656611,"user_tz":-120,"elapsed":290,"user":{"displayName":"Giorgos Siolas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjxnZOAObbc3X0z9X2rs1N_1geznqhrotkq3KF-p_M=s64","userId":"10127542075805046236"}}},"source":["from deap import tools\n","toolbox = base.Toolbox()\n","toolbox.register(\"mate\", tools.cxOnePoint)\n","toolbox.register(\"mutate\", tools.mutGaussian, mu=0.0, std=1.0)"],"execution_count":5,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"VeCFDBArH2bx"},"source":["Αρχικοποιούμε ένα στιγμιότυπο *toolbox* και του αναθέτουμε με τη register ένα τελεστή διασταύρωσης και ένα τελεστή μετάλλαξης. Η register απαιτεί τουλάχιστον δύο ορίσματα, το όνομα που δίνουμε στον τελεστή και τη συνάρτηση που τον υλοποιεί. Τα επόμενα ορίσματα μπορούν να είναι παράμετροι του τελεστή. Εδώ χρησιμοποιούμε τις builtin cxOnePoint (διαστάυρωση ενός σημείου) και mutGausian (γκαουσιανή μετάλλαξη με μέση τιμή 0 και απόκλιση 1 στο παράδειγμα).\n","\n","## Παραλληλισμός\n","Η DEAP μπορεί εύκολα να παραλληλοποιηθεί με τη χρήση της βιβλιοθήκης Scalable Concurent Operations ([SCOOP](https://github.com/soravux/scoop)) και να τρέξει σε κατανεμημένα συστήματα. Γιαυτό αρκεί κανείς να αντικαταστήσει στο toolbox τη στάνταρ συνάρτηση `map` της Python (που εφαρμόζει μια συνάρτηση σε κάθε στοιχείο μιας λίστας) με τη συνάρτηση map του SCOOP. "]},{"cell_type":"code","metadata":{"id":"7_MszMTjH2by","colab":{"base_uri":"https://localhost:8080/","height":346},"executionInfo":{"status":"ok","timestamp":1638783772783,"user_tz":-120,"elapsed":5041,"user":{"displayName":"Giorgos Siolas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjxnZOAObbc3X0z9X2rs1N_1geznqhrotkq3KF-p_M=s64","userId":"10127542075805046236"}},"outputId":"0b4e9b3b-c7eb-4900-e796-87048ca8fcc0"},"source":["! pip install -U scoop\n","\n","from scoop import futures\n","toolbox.register(\"map\", futures.map)"],"execution_count":6,"outputs":[{"output_type":"stream","name":"stdout","text":["Collecting scoop\n"," Downloading scoop-0.7.1.1.tar.gz (603 kB)\n","\u001b[?25l\r\u001b[K |▌ | 10 kB 16.9 MB/s eta 0:00:01\r\u001b[K |█ | 20 kB 20.1 MB/s eta 0:00:01\r\u001b[K |█▋ | 30 kB 22.7 MB/s eta 0:00:01\r\u001b[K |██▏ | 40 kB 25.2 MB/s eta 0:00:01\r\u001b[K |██▊ | 51 kB 27.2 MB/s eta 0:00:01\r\u001b[K |███▎ | 61 kB 28.3 MB/s eta 0:00:01\r\u001b[K |███▉ | 71 kB 26.8 MB/s eta 0:00:01\r\u001b[K |████▍ | 81 kB 27.1 MB/s eta 0:00:01\r\u001b[K |█████ | 92 kB 27.3 MB/s eta 0:00:01\r\u001b[K |█████▍ | 102 kB 28.6 MB/s eta 0:00:01\r\u001b[K |██████ | 112 kB 28.6 MB/s eta 0:00:01\r\u001b[K |██████▌ | 122 kB 28.6 MB/s eta 0:00:01\r\u001b[K |███████ | 133 kB 28.6 MB/s eta 0:00:01\r\u001b[K |███████▋ | 143 kB 28.6 MB/s eta 0:00:01\r\u001b[K |████████▏ | 153 kB 28.6 MB/s eta 0:00:01\r\u001b[K |████████▊ | 163 kB 28.6 MB/s eta 0:00:01\r\u001b[K |█████████▎ | 174 kB 28.6 MB/s eta 0:00:01\r\u001b[K |█████████▉ | 184 kB 28.6 MB/s eta 0:00:01\r\u001b[K |██████████▎ | 194 kB 28.6 MB/s eta 0:00:01\r\u001b[K |██████████▉ | 204 kB 28.6 MB/s eta 0:00:01\r\u001b[K |███████████▍ | 215 kB 28.6 MB/s eta 0:00:01\r\u001b[K |████████████ | 225 kB 28.6 MB/s eta 0:00:01\r\u001b[K |████████████▌ | 235 kB 28.6 MB/s eta 0:00:01\r\u001b[K |█████████████ | 245 kB 28.6 MB/s eta 0:00:01\r\u001b[K |█████████████▋ | 256 kB 28.6 MB/s eta 0:00:01\r\u001b[K |██████████████▏ | 266 kB 28.6 MB/s eta 0:00:01\r\u001b[K |██████████████▊ | 276 kB 28.6 MB/s eta 0:00:01\r\u001b[K |███████████████▏ | 286 kB 28.6 MB/s eta 0:00:01\r\u001b[K |███████████████▊ | 296 kB 28.6 MB/s eta 0:00:01\r\u001b[K |████████████████▎ | 307 kB 28.6 MB/s eta 0:00:01\r\u001b[K |████████████████▉ | 317 kB 28.6 MB/s eta 0:00:01\r\u001b[K |█████████████████▍ | 327 kB 28.6 MB/s eta 0:00:01\r\u001b[K |██████████████████ | 337 kB 28.6 MB/s eta 0:00:01\r\u001b[K |██████████████████▌ | 348 kB 28.6 MB/s eta 0:00:01\r\u001b[K |███████████████████ | 358 kB 28.6 MB/s eta 0:00:01\r\u001b[K |███████████████████▋ | 368 kB 28.6 MB/s eta 0:00:01\r\u001b[K |████████████████████ | 378 kB 28.6 MB/s eta 0:00:01\r\u001b[K |████████████████████▋ | 389 kB 28.6 MB/s eta 0:00:01\r\u001b[K |█████████████████████▏ | 399 kB 28.6 MB/s eta 0:00:01\r\u001b[K |█████████████████████▊ | 409 kB 28.6 MB/s eta 0:00:01\r\u001b[K |██████████████████████▎ | 419 kB 28.6 MB/s eta 0:00:01\r\u001b[K |██████████████████████▉ | 430 kB 28.6 MB/s eta 0:00:01\r\u001b[K |███████████████████████▍ | 440 kB 28.6 MB/s eta 0:00:01\r\u001b[K |████████████████████████ | 450 kB 28.6 MB/s eta 0:00:01\r\u001b[K |████████████████████████▌ | 460 kB 28.6 MB/s eta 0:00:01\r\u001b[K |█████████████████████████ | 471 kB 28.6 MB/s eta 0:00:01\r\u001b[K |█████████████████████████▌ | 481 kB 28.6 MB/s eta 0:00:01\r\u001b[K |██████████████████████████ | 491 kB 28.6 MB/s eta 0:00:01\r\u001b[K |██████████████████████████▋ | 501 kB 28.6 MB/s eta 0:00:01\r\u001b[K |███████████████████████████▏ | 512 kB 28.6 MB/s eta 0:00:01\r\u001b[K |███████████████████████████▊ | 522 kB 28.6 MB/s eta 0:00:01\r\u001b[K |████████████████████████████▎ | 532 kB 28.6 MB/s eta 0:00:01\r\u001b[K |████████████████████████████▉ | 542 kB 28.6 MB/s eta 0:00:01\r\u001b[K |█████████████████████████████▍ | 552 kB 28.6 MB/s eta 0:00:01\r\u001b[K |█████████████████████████████▉ | 563 kB 28.6 MB/s eta 0:00:01\r\u001b[K |██████████████████████████████▍ | 573 kB 28.6 MB/s eta 0:00:01\r\u001b[K |███████████████████████████████ | 583 kB 28.6 MB/s eta 0:00:01\r\u001b[K |███████████████████████████████▌| 593 kB 28.6 MB/s eta 0:00:01\r\u001b[K |████████████████████████████████| 603 kB 28.6 MB/s \n","\u001b[?25hRequirement already satisfied: greenlet>=0.3.4 in /usr/local/lib/python3.7/dist-packages (from scoop) (1.1.2)\n","Requirement already satisfied: pyzmq>=13.1.0 in /usr/local/lib/python3.7/dist-packages (from scoop) (22.3.0)\n","Collecting argparse>=1.1\n"," Downloading argparse-1.4.0-py2.py3-none-any.whl (23 kB)\n","Building wheels for collected packages: scoop\n"," Building wheel for scoop (setup.py) ... \u001b[?25l\u001b[?25hdone\n"," Created wheel for scoop: filename=scoop-0.7.1.1-py3-none-any.whl size=72145 sha256=d269e37d94b87e85c70cfd868b475748fbc0f24996acc683a3b0d5b1d2478736\n"," Stored in directory: /root/.cache/pip/wheels/24/19/e9/6e3e7c0323cc36bf1e4993d69b2db27d6b4e806ed57d411f44\n","Successfully built scoop\n","Installing collected packages: argparse, scoop\n","Successfully installed argparse-1.4.0 scoop-0.7.1.1\n"]},{"output_type":"display_data","data":{"application/vnd.colab-display-data+json":{"pip_warning":{"packages":["argparse"]}}},"metadata":{}}]},{"cell_type":"markdown","metadata":{"id":"7kM6PDpxH2b6"},"source":["Περισσότερες πληροφορίες για τη SCOOP και παραδείγματα [εδώ](http://deap.readthedocs.io/en/master/tutorials/basic/part4.html) και [εδώ](http://scoop.readthedocs.io/en/latest/usage.html). \n","\n","\n","Η DEAP μπορεί επίσης να χρησιμοποιήσει τη map της βιβλιοθήκης multiprocessing για να τρέξει παράλληλα σε πολλούς πυρήνες ενός μηχάνηματος:"]},{"cell_type":"code","metadata":{"id":"bTQoZNHWH2b7","executionInfo":{"status":"ok","timestamp":1638783787741,"user_tz":-120,"elapsed":265,"user":{"displayName":"Giorgos Siolas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjxnZOAObbc3X0z9X2rs1N_1geznqhrotkq3KF-p_M=s64","userId":"10127542075805046236"}}},"source":["import multiprocessing\n","pool = multiprocessing.Pool()\n","toolbox.register(\"map\", pool.map)"],"execution_count":7,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"agLU9CQNH2cB"},"source":["Στην πράξη στα cloud δεν θα χρησιμοποιήσουμε αυτές τις βιβλιοθήκες αλλά τοπικά είναι πολύ αποτελεσματικές ανάλογα τους διαθέσιμους πόρους.\n","\n","# Παράδειγμα 1: επίλυση του προβλήματος OneMax\n","\n","Το πρόβλημα *OneMax* (ή BitCounting) είναι ένα πολύ απλό πρόβλημα που συνίσταται στο να μεγιστοποιηθεί ο αριθμός των bits \"1\" σε μια δυαδική συμβολοσειρά. Πιο τυπικά, το πρόβλημα περιγράφεται με την αναζήτηση μιας συμβολοσειράς \n","$\\vec{x}=\\{x_{1},x_{2},\\ldots{},x_{N}\\}$, με $x_{i}\\in \\{0,1\\}$,\n","τέτοια που να μεγιστοποιεί την ακόλουθη εξίσωση:\n","\n","\\begin{equation}\n","F(\\vec{x}) = \\sum_{i=1}^{N}{x_{i}}\n","\\end{equation}\n","\n","Η βέλτιστη λύση είναι προφανώς $x_{i}=1$ για $i=1..N$.\n","\n","Αρχικά ορίζουμε μια καταλληλότητα προς μεγιστοποίηση:"]},{"cell_type":"code","metadata":{"id":"QGdCk9ynH2cC","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1638783871083,"user_tz":-120,"elapsed":309,"user":{"displayName":"Giorgos Siolas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjxnZOAObbc3X0z9X2rs1N_1geznqhrotkq3KF-p_M=s64","userId":"10127542075805046236"}},"outputId":"2717d8c7-133f-4eb4-8147-b2588ed7dc29"},"source":["creator.create(\"FitnessMax\", base.Fitness, weights=(1.0,))\n","creator.create(\"Individual\", list, fitness=creator.FitnessMax)"],"execution_count":8,"outputs":[{"output_type":"stream","name":"stderr","text":["/usr/local/lib/python3.7/dist-packages/deap/creator.py:141: RuntimeWarning: A class named 'Individual' has already been created and it will be overwritten. Consider deleting previous creation of that class or rename it.\n"," RuntimeWarning)\n"]}]},{"cell_type":"markdown","metadata":{"id":"p20kAzIMH2cH"},"source":["Στη συνέχεια θα δημιουργήσουμε τις κλάσεις των ατόμων και του πληθυσμού μας:"]},{"cell_type":"code","metadata":{"id":"ehefFqROH2cI","executionInfo":{"status":"ok","timestamp":1638783947777,"user_tz":-120,"elapsed":295,"user":{"displayName":"Giorgos Siolas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjxnZOAObbc3X0z9X2rs1N_1geznqhrotkq3KF-p_M=s64","userId":"10127542075805046236"}}},"source":["import random\n","\n","toolbox = base.Toolbox()\n","# Attribute generator \n","toolbox.register(\"attr_bool\", random.randint, 0, 1)\n","# Structure initializers\n","toolbox.register(\"individual\", tools.initRepeat, creator.Individual, toolbox.attr_bool, 100)\n","toolbox.register(\"population\", tools.initRepeat, list, toolbox.individual)"],"execution_count":9,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"bovacLK-H2cM"},"source":["Στο μπλοκ αυτό εγγράψαμε μια συνάρτηση δημιουργίας ενός χαρακτηριστικού \"attr_bool\" που παίρνει μια τυχαία δυαδική τιμή. Στη συνέχεια θα δημιουργήσουμε την κλάση των ατόμων χρησιμοποιώντας την `initRepeat()`. Η συνάρτηση αυτή επιστρέφει ένα άτομο - λίστα με καταλληλότητα προς μεγιστοποίηση (μέσω της κληρονομιάς του τύπου από το \"Individual\" και της \"FitnessMax\") που προκύπτει αν καλούσαμε την \"attr_bool\" 100 φόρες.\n","\n","Παρόμοια χρησιμοποιούμε την initRepeat για να φτιάξουμε τον πληθυσμό ο οποίος είναι μια λίστα με τα individual που μόλις ορίσαμε. Εδώ δεν ορίζουμε το μήκος της λίστας. \n","\n","Μπορούμε να δούμε πως λειτουργεί η κλήση αυτών των συναρτήσεων:"]},{"cell_type":"code","metadata":{"id":"GVqsdDRNH2cO","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1638783953592,"user_tz":-120,"elapsed":308,"user":{"displayName":"Giorgos Siolas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjxnZOAObbc3X0z9X2rs1N_1geznqhrotkq3KF-p_M=s64","userId":"10127542075805046236"}},"outputId":"180a213a-0067-4a27-e317-4a68f671216a"},"source":["bit = toolbox.attr_bool()\n","ind = toolbox.individual()\n","pop = toolbox.population(n=3)\n","\n","print(\"bit is of type %s and has value\\n%s\" % (type(bit), bit))\n","print(\"ind is of type %s and contains %d bits\\n%s\" % (type(ind), len(ind), ind))\n","print(\"pop is of type %s and contains %d individuals\\n%s\" % (type(pop), len(pop), pop))"],"execution_count":10,"outputs":[{"output_type":"stream","name":"stdout","text":["bit is of type and has value\n","1\n","ind is of type and contains 100 bits\n","[0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1]\n","pop is of type and contains 3 individuals\n","[[0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0], [0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0], [1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1]]\n"]}]},{"cell_type":"markdown","metadata":{"id":"Bk7W0ZRMH2cT"},"source":["Στη συνέχεια ορίζουμε τη συνάρτηση καταλληλότητας που πολύ απλά αθροίζει τους άσους του κάθε ατόμου:"]},{"cell_type":"code","metadata":{"id":"UIozesd-H2cV","executionInfo":{"status":"ok","timestamp":1638783993478,"user_tz":-120,"elapsed":282,"user":{"displayName":"Giorgos Siolas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjxnZOAObbc3X0z9X2rs1N_1geznqhrotkq3KF-p_M=s64","userId":"10127542075805046236"}}},"source":["def evalOneMax(individual):\n"," return sum(individual),"],"execution_count":11,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"YzlR1d0gH2cZ"},"source":["Σημειώστε και πάλι ότι επιστρέφουμε μια πλειάδα η οποία έχει μόνο ένα στοιχείο γιατι κάνουμε μονο κριτηριακή βελτιστοποίηση.\n","\n","Προχωράμε στον ορισμό της εργαλειοθήκης για το πρόβλημα:"]},{"cell_type":"code","metadata":{"id":"lZqIxCx8H2cb","executionInfo":{"status":"ok","timestamp":1638784128325,"user_tz":-120,"elapsed":293,"user":{"displayName":"Giorgos Siolas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjxnZOAObbc3X0z9X2rs1N_1geznqhrotkq3KF-p_M=s64","userId":"10127542075805046236"}}},"source":["toolbox.register(\"evaluate\", evalOneMax)\n","toolbox.register(\"mate\", tools.cxTwoPoint)\n","toolbox.register(\"mutate\", tools.mutFlipBit, indpb=0.10)\n","toolbox.register(\"select\", tools.selTournament, tournsize=3)"],"execution_count":12,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"PN6e2nHOH2ci"},"source":["Στην πρώτη γραμμή εγγράφουμε ως συνάρτηση καταλληλότητας \"evaluate\" την `evalOneMax` που εμείς ορίσαμε προηγουμένως. \n","\n","Στη συνέχεια εγγράφουμε ως \"mate\" τον τελεστή διασταύρωσης `cxTwoPoint` που κάνει διασταύρωση σε δύο σημεία. \n","\n","Στη συνέχεια ορίζουμε ως τελεστή μετάλλαξης \"mutate\" την αντιστροφή bit `mutFlipBit`. Προσοχή, η παράμετρος indpb (independent probability) δεν είναι η πιθανότητα μετάλλαξης ενός ατόμου (mutation probability) αλλά η πιθανότητα του κάθε bit χωριστά να υποστεί μετάλλαξη, εφόσον επιλεχθεί για μετάλλαξη το άτομο. Στη συγκεκριμένη περίπτωση περιμένουμε να αλλάξει το 10% των bits. \n","\n","\n","Τέλος επιλέγουμε εγγράφουμε τον τελεστή επιλογής \"select\" που χρησιμοποιεί την `selTournament` με μέγεθος διοργάνωσης 3. Αυτό σημαίνει ότι διαλέγουμε 3 τυχαία άτομα του πληθυσμού, τα συγκρίνουμε και κρατάμε το καλύτερο (ένας ακόμη τρόπος να υλοποιήσουμε το survival of the fittest). Γενικά η selTournament επιστρέφει αναφορές (references) και όχι τα ίδια τα άτομα (θα μας χρειαστεί αργότερα).\n","\n","Μπορείτε να δείτε όλους τους τελεστές στο [tools library reference](http://deap.readthedocs.io/en/master/api/tools.html) της DEAP.\n","\n","Σημειώστε ότι οι τελεστές εκτελούνται inplace στα άτομα στα οποία καλούνται:"]},{"cell_type":"code","metadata":{"id":"sC-VJXwPH2cl","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1638784158215,"user_tz":-120,"elapsed":286,"user":{"displayName":"Giorgos Siolas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjxnZOAObbc3X0z9X2rs1N_1geznqhrotkq3KF-p_M=s64","userId":"10127542075805046236"}},"outputId":"170351c6-33be-48e2-ae11-4300d381065c"},"source":["ind = toolbox.individual()\n","print(ind)\n","toolbox.mutate(ind)\n","print(ind)"],"execution_count":13,"outputs":[{"output_type":"stream","name":"stdout","text":["[0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0]\n","[0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0]\n"]}]},{"cell_type":"markdown","metadata":{"id":"Rbx2QtktH2cu"},"source":["Που σημαίνει ότι αν ένα άτομο δεν αντιγραφεί πρωτού το τροποποιήσουμε η αρχική του τιμή χάνεται. Η αντιγραφή γίνεται με την `clone`. Σημειώστε επίσης ότι δύο αντικείμενα είναι διαφορετικά ακόμα και αν έχουν ίδια χρωμοσώματα."]},{"cell_type":"code","metadata":{"id":"uSg6ajE9H2cx","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1638784189755,"user_tz":-120,"elapsed":397,"user":{"displayName":"Giorgos Siolas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjxnZOAObbc3X0z9X2rs1N_1geznqhrotkq3KF-p_M=s64","userId":"10127542075805046236"}},"outputId":"e36b3095-7d25-40de-b14c-c2d438cca13e"},"source":["mutant = toolbox.clone(ind)\n","print(mutant is ind)\n","print(mutant == ind)\n","# o mutant δεν είναι το ίδιο άτομο με τον ind αλλά έχει το ίδιο χρωμόσωμα"],"execution_count":14,"outputs":[{"output_type":"stream","name":"stdout","text":["False\n","True\n"]}]},{"cell_type":"markdown","metadata":{"id":"YLpianfQH2c2"},"source":["Μπορούμε πλέον πολύ απλά να τρέξουμε τον βασικό γενετικό αλγόριθμο `eaSimple` με import από το [algorithms](http://deap.readthedocs.io/en/master/api/algo.html) του DEAP. Θα αρχικόποιήσουμε έναν πληθυσμό n ατόμων, και θα τρέξουμε την eaSimple ορίζοντας πιθανότητες διασταύρωσης (cxpb), μετάλλαξης (mutpb) καθώς και τον αριθμό των γενεών (ngen). Τέλος θα τυπώσουμε το καλύτερο άτομο του τελικού πληθυσμού με την `selBest`"]},{"cell_type":"code","metadata":{"id":"ZqKka1hhH2c4","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1638784299808,"user_tz":-120,"elapsed":1885,"user":{"displayName":"Giorgos Siolas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjxnZOAObbc3X0z9X2rs1N_1geznqhrotkq3KF-p_M=s64","userId":"10127542075805046236"}},"outputId":"fb693d66-6a98-4b7e-95ac-240d30dbc632"},"source":["from deap import algorithms\n","if __name__ == \"__main__\":\n"," pop = toolbox.population(n=300)\n"," algorithms.eaSimple(pop, toolbox, cxpb=0.5, mutpb=0.2, ngen=50, verbose=False)\n"," print(tools.selBest(pop, k=1))"],"execution_count":15,"outputs":[{"output_type":"stream","name":"stdout","text":["[[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]\n"]}]},{"cell_type":"markdown","metadata":{"id":"-iZn1IJVH2dC"},"source":["Το `if __name__ == \"__main__\":` επιτρέπει την εκτέλεση του κώδικα που το ακολουθεί μόνο όταν το script (εδώ το notebook) είναι η κυρίως ρουτίνα που εκτελείται και που ονομάζεται αυτόματα \"\\_\\_main\\_\\_\". Αυτό διασφαλίζει ότι σε παραλληλία η κυρίως συνάρτηση δεν θα τρέξει από διεργασίες-παιδιά. \n","\n","Προκειμένου να έχουμε καλύτερη εικόνα της συμπεριφοράς του αλγόριθμου θα ξαναγράψουμε το κυρίως πρόγραμμα ως εξής:"]},{"cell_type":"code","metadata":{"id":"yoSNTPHCH2dE","executionInfo":{"status":"ok","timestamp":1638784358655,"user_tz":-120,"elapsed":327,"user":{"displayName":"Giorgos Siolas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjxnZOAObbc3X0z9X2rs1N_1geznqhrotkq3KF-p_M=s64","userId":"10127542075805046236"}}},"source":["def ea_with_stats():\n"," import numpy\n"," \n"," pop = toolbox.population(n=300)\n"," hof = tools.HallOfFame(1)\n"," stats = tools.Statistics(lambda ind: ind.fitness.values)\n"," stats.register(\"avg\", numpy.mean)\n"," stats.register(\"min\", numpy.min)\n"," stats.register(\"max\", numpy.max)\n"," \n"," pop, logbook = algorithms.eaSimple(pop, toolbox, cxpb=0.5, mutpb=0.2, ngen=40, stats=stats, halloffame=hof, verbose=True)\n"," \n"," return pop, logbook, hof"],"execution_count":16,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"i91d0q1qH2dI"},"source":["Η `HallofFame` κρατάει τα k καλύτερα άτομα που έχουν εμφανιστεί οποιαδήποτε στιγμή μέσα στον πληθυσμό. Την αρχικοποιούμε ως \"hof\"\n","\n","H `Statistics` υπολογίζει στατιστικά για μια οποιαδήποτε λίστα αντικειμένων. Εδώ, για κάθε πληθυσμό, θα χρησιμοποιήσουμε τη λίστα με της τιμές καταλληλότητας όλων των ατόμων του πληθυσμού και θα την αρχικοποιήσουμε ως \"stats\". Στη συνέχεια θα εγράψουμε στη stats τρεις αριθμητικες πράξεις (μέσω της numpy) που θα εκτελούνται στη λίστα με τις καταλληλότητες των ατόμων: μέση, ελάχιστη και μέγιστη τιμή της καταλληλότητας του πληθυσμού.\n","\n","Τα επιπλέον ορίσματα στην eaSimple λειτουργούν ως εξής:\n","- το stats=stats ορίζει ποια στατιστικά θα υπολογίζονται σε κάθε γενιά\n","- το halloffame=hof το αντικείμενο HallOfFame που θα αποθηκεύεται το τυχόν συνολικά βέλτιστο άτομο\n","- το verbose=True θα τυπώνει στην οθόνη τον αριθμό της γενιάς, τον αριθμό αποτιμήσεων καταλληλότητας που χρειάστηκε να γίνουν και στη συνέχεια τα στατιστικά της \"stats\". Σημειώστε ότι μετά την αρχικοποίηση η αποτίμηση καταλληλότητας γίνεται μόνο για άτομα που έχουν υποστεί αλλαγή.\n","\n","Τέλος με την έξοδο pop, logbook η eaSimple μας επιστρέφει τον τελικό πληθυσμό και το αντικείμενο logbook που περιέχει τις στατιστικές που έχουμε ορίσει για όλες τις γενιές.\n","\n","Μπορείτε να δείτε όλες τις παραπάνω συναρτήσεις στο [library reference](http://deap.readthedocs.io/en/master/api/index.html)\n","\n","Στο επόμενο μπλοκ τρέχουμε προστατευμένη την ea_with_stats, τυπώνουμε το καλύτερο άτομο και χρησιμοποιόντας το logbook \"log\" τυπώνουμε την εξέλιξη των τριών μετρικών (avg, min, max) ως συνάρτηση των διαδοχικών γενεών. "]},{"cell_type":"code","metadata":{"scrolled":true,"id":"phgRSeALH2dK","colab":{"base_uri":"https://localhost:8080/","height":1000},"executionInfo":{"status":"ok","timestamp":1638784398434,"user_tz":-120,"elapsed":2273,"user":{"displayName":"Giorgos Siolas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjxnZOAObbc3X0z9X2rs1N_1geznqhrotkq3KF-p_M=s64","userId":"10127542075805046236"}},"outputId":"caa2eb29-94fc-4a5e-8333-1d03ae08957b"},"source":["if __name__ == \"__main__\":\n"," pop, log, hof = ea_with_stats()\n"," print(\"Best individual is: %s\\nwith fitness: %s\" % (hof[0], hof[0].fitness))\n"," \n"," %matplotlib inline\n"," import matplotlib.pyplot as plt\n"," gen, avg, min_, max_ = log.select(\"gen\", \"avg\", \"min\", \"max\")\n"," plt.plot(gen, avg, label=\"average\")\n"," plt.plot(gen, min_, label=\"minimum\")\n"," plt.plot(gen, max_, label=\"maximum\")\n"," plt.xlabel(\"Generation\")\n"," plt.ylabel(\"Fitness\")\n"," plt.legend(loc=\"lower right\")\n"," plt.show()"],"execution_count":17,"outputs":[{"output_type":"stream","name":"stdout","text":["gen\tnevals\tavg \tmin\tmax\n","0 \t300 \t49.3433\t35 \t66 \n","1 \t192 \t53.12 \t41 \t65 \n","2 \t200 \t56.19 \t44 \t72 \n","3 \t175 \t58.8033\t50 \t72 \n","4 \t205 \t61.54 \t49 \t73 \n","5 \t177 \t64.6433\t54 \t73 \n","6 \t169 \t67.2867\t57 \t80 \n","7 \t182 \t69.31 \t59 \t80 \n","8 \t167 \t71.34 \t59 \t82 \n","9 \t176 \t73.2533\t61 \t82 \n","10 \t183 \t75.2967\t62 \t83 \n","11 \t182 \t77.0733\t67 \t86 \n","12 \t170 \t78.6767\t63 \t91 \n","13 \t170 \t80.3167\t66 \t89 \n","14 \t172 \t82.0767\t68 \t89 \n","15 \t175 \t83.4733\t70 \t89 \n","16 \t197 \t84.6567\t70 \t91 \n","17 \t181 \t86 \t71 \t93 \n","18 \t169 \t86.8433\t73 \t93 \n","19 \t172 \t88.0933\t75 \t93 \n","20 \t205 \t88.74 \t75 \t94 \n","21 \t189 \t89.7767\t73 \t95 \n","22 \t171 \t90.6767\t75 \t96 \n","23 \t172 \t91.4267\t77 \t96 \n","24 \t182 \t91.8033\t79 \t96 \n","25 \t169 \t92.6633\t78 \t97 \n","26 \t197 \t92.9233\t78 \t98 \n","27 \t177 \t93.8167\t79 \t98 \n","28 \t199 \t93.7833\t81 \t98 \n","29 \t178 \t94.55 \t82 \t98 \n","30 \t170 \t95.1067\t78 \t99 \n","31 \t181 \t95.8567\t75 \t99 \n","32 \t201 \t95.7467\t82 \t100\n","33 \t165 \t96.1 \t81 \t100\n","34 \t183 \t96.61 \t80 \t100\n","35 \t174 \t97.13 \t79 \t100\n","36 \t173 \t97.4767\t82 \t100\n","37 \t178 \t97.5567\t82 \t100\n","38 \t182 \t97.76 \t82 \t100\n","39 \t163 \t98.03 \t83 \t100\n","40 \t188 \t97.7967\t79 \t100\n","Best individual is: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n","with fitness: (100.0,)\n"]},{"output_type":"display_data","data":{"image/png":"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\n","text/plain":["