# Notebook from nlazarevic/gi-2021-etf
Path: notebooks/04_Boyer_Moore.ipynb

The following gray box contains several functions that preprocess the pattern *P* and create the tables needed to apply the bad character and good suffix rules.  We won't discuss these, but if you are interested, see Chapter 2 of:

Gusfield, Dan. Algorithms on strings, trees and sequences: computer science and computational biology. Cambridge university press, 1997._____no_output_____

import string

def z_array(s):
    """ Use Z algorithm (Gusfield theorem 1.4.1) to preprocess s """
    assert len(s) > 1
    z = [len(s)] + [0] * (len(s)-1)
    # Initial comparison of s[1:] with prefix
    for i in range(1, len(s)):
        if s[i] == s[i-1]:
            z[1] += 1
        else:
            break
    r, l = 0, 0
    if z[1] > 0:
        r, l = z[1], 1
    for k in range(2, len(s)):
        assert z[k] == 0
        if k > r:
            # Case 1
            for i in range(k, len(s)):
                if s[i] == s[i-k]:
                    z[k] += 1
                else:
                    break
            r, l = k + z[k] - 1, k
        else:
            # Case 2
            # Calculate length of beta
            nbeta = r - k + 1
            zkp = z[k - l]
            if nbeta > zkp:
                # Case 2a: Zkp wins
                z[k] = zkp
            else:
                # Case 2b: Compare characters just past r
                nmatch = 0
                for i in range(r+1, len(s)):
                    if s[i] == s[i - k]:
                        nmatch += 1
                    else:
                        break
                l, r = k, r + nmatch
                z[k] = r - k + 1
    return z

def n_array(s):
    """ Compile the N array (Gusfield theorem 2.2.2) from the Z array """
    return z_array(s[::-1])[::-1]

def big_l_prime_array(p, n):
    """ Compile L' array (Gusfield theorem 2.2.2) using p and N array.
        L'[i] = largest index j less than n such that N[j] = |P[i:]| """
    lp = [0] * len(p)
    for j in range(len(p)-1):
        i = len(p) - n[j]
        if i < len(p):
            lp[i] = j + 1
    return lp

def big_l_array(p, lp):
    """ Compile L array (Gusfield theorem 2.2.2) using p and L' array.
        L[i] = largest index j less than n such that N[j] >= |P[i:]| """
    l = [0] * len(p)
    l[1] = lp[1]
    for i in range(2, len(p)):
        l[i] = max(l[i-1], lp[i])
    return l

def small_l_prime_array(n):
    """ Compile lp' array (Gusfield theorem 2.2.4) using N array. """
    small_lp = [0] * len(n)
    for i in range(len(n)):
        if n[i] == i+1:  # prefix matching a suffix
            small_lp[len(n)-i-1] = i+1
    for i in range(len(n)-2, -1, -1):  # "smear" them out to the left
        if small_lp[i] == 0:
            small_lp[i] = small_lp[i+1]
    return small_lp

def good_suffix_table(p):
    """ Return tables needed to apply good suffix rule. """
    n = n_array(p)
    lp = big_l_prime_array(p, n)
    return lp, big_l_array(p, lp), small_l_prime_array(n)

def good_suffix_mismatch(i, big_l_prime, small_l_prime):
    """ Given a mismatch at offset i, and given L/L' and l' arrays,
        return amount to shift as determined by good suffix rule. """
    length = len(big_l_prime)
    assert i < length
    if i == length - 1:
        return 0
    i += 1  # i points to leftmost matching position of P
    if big_l_prime[i] > 0:
        return length - big_l_prime[i]
    return length - small_l_prime[i]

def good_suffix_match(small_l_prime):
    """ Given a full match of P to T, return amount to shift as
        determined by good suffix rule. """
    return len(small_l_prime) - small_l_prime[1]

def dense_bad_char_tab(p, amap):
    """ Given pattern string and list with ordered alphabet characters, create
        and return a dense bad character table.  Table is indexed by offset
        then by character. """
    tab = []
    nxt = [0] * len(amap)
    for i in range(0, len(p)):
        c = p[i]
        assert c in amap
        tab.append(nxt[:])
        nxt[amap[c]] = i+1
    return tab

class BoyerMoore(object):
    """ Encapsulates pattern and associated Boyer-Moore preprocessing. """

    def __init__(self, p, alphabet='ACGT'):
        self.p = p
        self.alphabet = alphabet
        # Create map from alphabet characters to integers
        self.amap = {}
        for i in range(len(self.alphabet)):
            self.amap[self.alphabet[i]] = i
        # Make bad character rule table
        self.bad_char = dense_bad_char_tab(p, self.amap)
        # Create good suffix rule table
        _, self.big_l, self.small_l_prime = good_suffix_table(p)

    def bad_character_rule(self, i, c):
        """ Return # skips given by bad character rule at offset i """
        assert c in self.amap
        ci = self.amap[c]
        assert i > (self.bad_char[i][ci]-1)
        return i - (self.bad_char[i][ci]-1)

    def good_suffix_rule(self, i):
        """ Given a mismatch at offset i, return amount to shift
            as determined by (weak) good suffix rule. """
        length = len(self.big_l)
        assert i < length
        if i == length - 1:
            return 0
        i += 1  # i points to leftmost matching position of P
        if self.big_l[i] > 0:
            return length - self.big_l[i]
        return length - self.small_l_prime[i]

    def match_skip(self):
        """ Return amount to shift in case where P matches T """
        return len(self.small_l_prime) - self.small_l_prime[1]_____no_output_____

Let's make sure our rules give the expected results._____no_output_____

# GCTAGCTCTACGAGTCTA
p = 'TCAA'
p_bm = BoyerMoore(p, alphabet='ACGT')
p_bm.bad_character_rule(3, 'T')_____no_output_____# GCTAGCTCTACGAGTCTA
# ACTA
p = 'ACTA'
p_bm = BoyerMoore(p, alphabet='ACGT')
p_bm.good_suffix_rule(0)_____no_output_____# ACACGCTCTACGAGTCTA
# ACAC
p = 'ACACT'
p_bm = BoyerMoore(p, alphabet='ACGT')
p_bm.match_skip()_____no_output_____def boyer_moore(p, p_bm, t):
    """ Do Boyer-Moore matching """
    i = 0
    occurrences = []
    cnt = 0
    while i < len(t) - len(p) + 1:
        shift = 1
        mismatched = False
        for j in range(len(p)-1, -1, -1):
            cnt += 1
            if p[j] != t[i+j]:
                skip_bc = p_bm.bad_character_rule(j, t[i+j])
                skip_gs = p_bm.good_suffix_rule(j)
                shift = max(shift, skip_bc, skip_gs)
                mismatched = True
                break
        if not mismatched:
            occurrences.append(i)
            skip_gs = p_bm.match_skip()
            shift = max(shift, skip_gs)
        i += shift
    return occurrences_____no_output_____t = 'CTTATCGATGAAACTGAATCGTACTCAGGTCA'
p = 'TGAAT'

p_bm = BoyerMoore(p, alphabet='ACGT')
boyer_moore(p, p_bm, t)_____no_output_____t = 'GCTAGCTCTACGAGTCTA'
p = 'TCTA'
p_bm = BoyerMoore(p, alphabet='ACGT')_____no_output_____boyer_moore(p, p_bm, t)_____no_output_____t = 'CTGAAGTACGATTATA'
p = 'TCTA'
p_bm = BoyerMoore(p, alphabet='ACGT')
boyer_moore(p, p_bm, t)_____no_output_____


# Notebook from suhasini-gesis/IWAAN
Path: 1_General_Metadata.ipynb

Welcome!

This  collection of notebooks lets you analyze the evolution of the revision history of a Wikipedia article (with live data). Among other things, it allows you to inspect **article- or editor-specific conflicts &  productivity.** The magic happens mostly in notebook 2

Each Notebook can be **explored like a Web app, without interacting with the code behind it**. But you can also - if you choose to - click on "Show solution" after each block and work with the code directly (green button).

---

**Please choose a small or medium-sized article for your first exploration to reduce computation times. ALL DATA IS FETCHED LIVE, so please be patient.** Filtering is done locally, so that will be quicker. To start you can, e.g., simply go with our default choice. Very large articles may lead to memory overload, depending on which Binder platform you are using. You could get more memory by shutting down some previous running notebooks.

When loaded the first time, all cells **should** run automatically and you should not see any code. If that is not the case, please just reload the tab in your browser. After chosing a new article, please rerun the cells/modules you want to use. _____no_output_____

from IPython.display import display, Javascript, HTML, clear_output
from ipywidgets import widgets, Output, interact, Layout
from ipywidgets.widgets import Dropdown
from datetime import datetime

## SOME EXTENTIONS ##
#%load_ext autoreload
%reload_ext autoreload
%autoreload 2
if 'the_page' not in locals():
    import pickle
    print("Loading default data...")
    the_page = pickle.load(open("data/the_page.p",'rb'))

display(Javascript('IPython.notebook.execute_cells_below()'))

display(Javascript('Jupyter.notebook.get_cells()'))_____no_output_____%%capture
%store -r the_page_____no_output_____

###  Modules Imported _____no_output_____

## Modules Imported ##

# Display
from IPython.display import display, Markdown as md, clear_output
from datetime import date
import urllib

# APIs
from wikiwho_wrapper import WikiWho
from external.wikipedia import WikipediaDV, WikipediaAPI
from external.wikimedia import WikiMediaDV, WikiMediaAPI
from external.xtools import XtoolsAPI, XtoolsDV

# Data Processing
import pickle
import pandas as pd

# Visualization tools
import qgrid
import matplotlib.pyplot as plt

# Page views timeline
from visualization.views_listener import ViewsListener

# Change actions timeline
from visualization.actions_listener import ActionsListener

# Conflicts visualization
from visualization.conflicts_listener import ConflictsListener, ConflictsActionListener
from visualization.calculator_listener import ConflictCalculatorListener

# Word cloud visualization
from visualization.wordcloud_listener import WCListener, WCActionsListener
from visualization.wordclouder import WordClouder

# Wikipedia talk pages visualization
from visualization.talks_listener import TalksListener
from visualization.topics_listener import TopicsListener

# Tokens ownership visualization
from visualization.owned_listener import OwnedListener

# Templates visualization
from visualization.templates_listener import ProtectListener, TemplateListener

# Metrics management
from metrics.conflict import ConflictManager
from metrics.token import TokensManager

# For language selection
from utils.lngselection import abbreviation, lng_listener_____no_output_____

---

# A. Selecting a Wikipedia article

Let's start: The default example is the article "The Camp of the Saints" (a novel). But you can enter/search an article of your choice and explore it as well. _____no_output_____

# the method that listens to the click event
def on_button_clicked(b):
    global wikipedia_dv
    global the_page
    # use the out widget so the output is overwritten when two or more
    # searches are performed
    with out:
        try:
            # query wikipedia
            wikipedia_dv = WikipediaDV(WikipediaAPI(lng=abbreviation(languageSelection.value)))
            search_result = wikipedia_dv.search_page(searchTerm.value)
            the_page = wikipedia_dv.get_page(search_result)
            %store the_page
            clear_output()
            display(md(f"The page that was found: **{the_page['title']}**"))
            display(md(f"Page id: **{the_page['page_id']}**"))
            url = f"{wikipedia_dv.api.base}action=query&titles={urllib.parse.quote_plus(the_page['title'])}&format=json"
            display(md(f"Metadata can be found in:"))
            print(url)
            #display(Javascript('Jupyter.notebook.execute_cells([8])'))

        except:
            clear_output()
            display(md(f'The page title *"{searchTerm.value}"* was not found'))
            #display(Javascript('Jupyter.notebook.execute_cells([8])'))

# Language selection.
languageSelection = Dropdown(options=['English', 'Deutsch', 'Español', 'Türkçe', 'Euskara'], value='English', description='Language:')

# by default display the last search
try:
    searchTerm = widgets.Text(the_page['title'], description='Page title:')
except:
    searchTerm = widgets.Text("The Camp of the Saints", description='Page title:')

# Update selected language
initial_select = widgets.interactive(lng_listener, lng=languageSelection, search_term=searchTerm)
display(initial_select.children[0])
display(initial_select.children[1])

# create and display the button
button = widgets.Button(description="Search")
example = md("e.g. *The Camp of the Saints*")
display(example, button)

# the output widget is used to remove the output after the search field
out = Output()
display(out)

# set the event
button.on_click(on_button_clicked)

# trigger the event with the default value
on_button_clicked(button)_____no_output_____

 Try yourself! This is what will happen when you click 'Search' button: _____no_output_____

%%script false --no-raise-error

### IMPORTANT NOTE: COMMENT THE ABOVE LINE TO EXECUTE THE CELL ###

### --------------------------------------------------------------------- ###
### TRY YOURSELF! THIS IS WHAT WILL HAPPEN WHEN YOU CLICK 'Search' BUTTON ###
### --------------------------------------------------------------------- ###

## This is the default data and used for initialization ##
the_page = pickle.load(open("data/the_page.p",'rb'))  # global
title_default = the_page['title']
print('The pre-filled value for the title:', title_default)

## The search term you have input ##

# this was extracted from the previous search box, searchTerm, which is built by
# e.g. searchTerm = widgets.Text(the_page['title'], description='Page title:'), you could
# know more about it through https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20List.html
search_language = languageSelection.value
language_for_api = abbreviation(search_language)

search_value = searchTerm.value
print('The language you use now:', search_language)
print('The value you input in search box:', search_value)

# of course you could also update the value here, like in the "Page title:" box above.
#search_value = 'Matrix Completion'

## query wikipedia using WikiWho API, more details please see:                ##
## https://github.com/gesiscss/wikiwho_demo/blob/master/external/wikipedia.py ##
## https://github.com/gesiscss/wikiwho_demo/blob/master/external/api.py       ##
wikipedia_dv = WikipediaDV(WikipediaAPI(lng=language_for_api)) # create an instance
result_after_search = wikipedia_dv.search_page(search_value)

print("The page that was found:", result_after_search)

## Get page id through get_page() method ##
the_page = wikipedia_dv.get_page(result_after_search)  # global
page_id = the_page['page_id']
print('Page id:', page_id)

## Metadata ##
url = f"{wikipedia_dv.api.base}action=query&titles={urllib.parse.quote_plus(result_after_search)}&format=json"
print('Metadata can be found in:', url)_____no_output_____

---

# B. General Statistics

Now, lets get live data of some general statistics (through Xtools API (1))_____no_output_____

def xtools_on_click(b):
    with out_xtools:
        clear_output()
        xtools_api = XtoolsAPI(lng=abbreviation(languageSelection.value))
        xtools_dv = XtoolsDV(xtools_api)
        page_info = xtools_dv.get_page_info(the_page['title'])
        page_info['assessment'] = page_info['assessment']['value'] if type(page_info['assessment']) != bool else page_info['assessment']

        page_info = page_info.to_frame('value').rename(index={
            'project': 'Project name',
            'page': 'Page name',
            'watchers': 'Watchers (2)',    'pageviews': f"Page Views (per {page_info['pageviews_offset']} days)",
            'revisions': 'Revisions',
            'editors': 'Editors',
            'author': 'Creator of the page',
            'created_at': 'Creation Date',
            'created_rev_id': 'Creation revision id',
            'modified_at': 'Last modified',
            'last_edit_id': 'Last revision id',
            'assessment': 'Content Assessment (3)',
        }).drop(index = ['pageviews_offset', 'author_editcount', 'secs_since_last_edit','elapsed_time'])

        display(md(f"***Page: {the_page['title']} ({abbreviation(languageSelection.value).upper()})***"))
        url = f"{xtools_dv.api.base}page/articleinfo/{xtools_dv.api.project}/" + urllib.parse.quote(the_page['title'])
        display(md(f"Metadata can be found in"))
        print(url)
        display(page_info)
        #display(Javascript('Jupyter.notebook.execute_cells([14])'))
        #display(Javascript('Jupyter.notebook.execute_cells([18])'))

# create and display the button
button = widgets.Button(description="Get Page Info")
display(button)

# the output widget is used to remove the output after the search field
out_xtools = Output()
display(out_xtools)

# set the event
button.on_click(xtools_on_click)

# trigger the event with the default value
xtools_on_click(button)_____no_output_____

**(1)** *A community-built service for article statistics at xtools.wmflabs.org* **(2)** *Users that added this page to their watchlist.* **(3)** *See [Wikipedia Content Assessment](https://en.wikipedia.org/wiki/Wikipedia:Content_assessment)*
_____no_output_____ Try yourself! This is what will happen when you click 'Get Page Info' button: _____no_output_____

%%script false --no-raise-error

### IMPORTANT NOTE: COMMENT THE ABOVE LINE TO EXECUTE THE CELL ###

### ----------------------------------------------------------------------------- ###
### TRY YOURSELF! THIS IS WHAT WILL HAPPEN WHEN YOU CLICK 'Get Page Info' BUTTON  ###
### ----------------------------------------------------------------------------- ###

## Define a Xtools instance, more details see:                             ##
## https://github.com/gesiscss/wikiwho_demo/blob/master/external/xtools.py ##
## https://github.com/gesiscss/wikiwho_demo/blob/master/external/api.py    ##
xtools_api = XtoolsAPI(lng=abbreviation(languageSelection.value))
xtools_dv = XtoolsDV(xtools_api)
print('Provided through the Xtools API (1)')

## The page you are insterested in ##
print('The page that was found:', the_page['title'], f'({abbreviation(languageSelection.value).upper()})')

## Get the page info through Xtools method get_page_info() ##
page_info = xtools_dv.get_page_info(the_page['title'])

## Metadata ##
url = f"{xtools_dv.api.base}page/articleinfo/{xtools_dv.api.project}/" + urllib.parse.quote(the_page['title'])
print("Metadata can be found in:", url)

## Use a dictionary to construct a pd.DataFrame to present the general info from Xtools ##
dict_for_df = {
    'Project name': page_info['project'], 'Page name': page_info['page'], 'Watchers(2)': page_info['watchers'],
    'Page Views (per 30 days)': page_info['pageviews'], 'Revisions': page_info['revisions'],
    'Editors': page_info['editors'], 'minor_edits': page_info['minor_edits'], 'Creator of the page': page_info['author'],
    'Creation Date': page_info['created_at'], 'Creation revision id': page_info['created_at'],
    'Last modified': page_info['modified_at'], 'Last revision id': page_info['last_edit_id'],
    'Content Assessment (3)': page_info['assessment']['value'] if type(page_info['assessment']) != bool else page_info['assessment']
}

df_info = pd.DataFrame.from_dict(dict_for_df, orient='index', columns=['value'])
display(df_info)

## Some footnotes ##
display(md('**(1)** *A community-built service for article statistics at xtools.wmflabs.org*'
           '**(2)** *Users that added this page to their watchlist.*'
           '**(3)** *See [Wikipedia Content Assessment](https://en.wikipedia.org/wiki/Wikipedia:Content_assessment)*'))_____no_output_____

---

# C. Page Views

Provided through the Wikimedia API_____no_output_____

def pageviews_button(b):
    with out_pageviews:
        clear_output()

        display(md(f"***Page: {the_page['title']} ({abbreviation(languageSelection.value).upper()})***"))
        # Query request
        wikimedia_api = WikiMediaAPI(lng=abbreviation(languageSelection.value))
        wikimedia_dv = WikiMediaDV(wikimedia_api)
        views = wikimedia_dv.get_pageviews(the_page['title'], 'daily')

        # Visualization
        listener = ViewsListener(views)
        inter_func = interact(listener.listen,
                         begin=Dropdown(options=views.timestamp),
                         end=Dropdown(options=views.timestamp.sort_values(ascending=False)),
                         granularity=Dropdown(options=['Yearly', 'Monthly', 'Weekly', 'Daily'], value='Monthly'))

        # raw data url
        start = 19900101
        today = datetime.today().strftime("%Y%m%d")
        end = int(today)
        article_name = urllib.parse.quote(the_page['title'])
        granularity = 'daily'

        url = (f'{wikimedia_dv.api.base}metrics/pageviews/per-article/{wikimedia_dv.api.project}/'
                f'all-access/all-agents/{article_name}/{granularity}/{start}/{end}')
        display(md(f"Metadata can be found in:"))
        print(url)

        # The df_plotted keeps a reference to the plotted data above
        pageviews_agg = listener.df_plotted['views'].agg({
                            'Total views': sum,
                            'Max views period': max,
                            'Min views period': min,
                            'Average views': min,}).to_frame('Value')
        display(pageviews_agg)

# create and display the button
button = widgets.Button(description="Get Pageviews", layout=Layout(width='150px'))
display(button)

# the output widget is used to remove the output after the search field
out_pageviews = Output()
display(out_pageviews)

# set the event
button.on_click(pageviews_button)

# trigger the event with the default value
pageviews_button(button)_____no_output_____

 Try yourself! This is what will happen when you click 'Get Pageviews' button: _____no_output_____

%%script false --no-raise-error

### IMPORTANT NOTE: COMMENT THE ABOVE LINE TO EXECUTE THE CELL ###

### ----------------------------------------------------------------------------- ###
### TRY YOURSELF! THIS IS WHAT WILL HAPPEN WHEN YOU CLICK 'Get Pageviews' BUTTON  ###
### ----------------------------------------------------------------------------- ###

## define a WikiMediaAPI instance, more details see:                          ##
## https://github.com/gesiscss/wikiwho_demo/blob/master/external/wikimedia.py ##
## https://github.com/gesiscss/wikiwho_demo/blob/master/external/api.py       ##

wikimedia_api = WikiMediaAPI(lng=abbreviation(languageSelection.value))
wikimedia_dv = WikiMediaDV(wikimedia_api)

## Page of insterest ##
print('The page that was found:', the_page['title'], f'({abbreviation(languageSelection.value).upper()})')

## get pageview counts for the article, more details see:                     ##
## https://github.com/gesiscss/wikiwho_demo/blob/master/external/wikimedia.py ##

views = wikimedia_dv.get_pageviews(the_page['title'], 'daily')

## Visualization, core visual code lies in ViewsListener, then the interact function    ##
## make it interactive, mode details see:                                               ##
## https://github.com/gesiscss/wikiwho_demo/blob/master/visualization/views_listener.py ##
## https://ipywidgets.readthedocs.io/en/latest/examples/Using%20Interact.html           ##

# Create a ViewListener instance with the page views counts
listener = ViewsListener(views)

# You could customize begin/end dates and granularity to generate different graphs
# e.g. begin='20160809', end='20191020', granularity='Monthly'
begin='20150701'
end='20210314'
granularity='Monthly' # 'Yearly', 'Monthly', 'Weekly' ,'Daily'

# Metadata
url = (f'{wikimedia_dv.api.base}metrics/pageviews/per-article/'
       f"{wikimedia_dv.api.project}/all-access/all-agents/{urllib.parse.quote(the_page['title'])}/{granularity.lower()}"
       f"/{int(begin)}/{int(end)}")
print('Metadata can be found in:', url)
print('(Note that: the time unit for the response data only supports granularity for "daily" and "monthly")\n')

# Time range you have selected
print('Time range you have selected:')
print('Start date:', begin)
print('End date:', end)
print('Granularity:', granularity)

# Visulization
listener.listen(begin, end, granularity)

# Pageviews aggregation data. Use the attribute "df_plotted".
pageviews_agg = listener.df_plotted['views'].agg({
                    'Total views': sum,
                    'Max views period': max,
                    'Min views period': min,
                    'Average views': min,}).to_frame('Value')

print('Total views of this page:', pageviews_agg['Value']['Total views'])
print('Max views during the selected period:', pageviews_agg['Value']['Max views period'])
print('Min views during the selected period:', pageviews_agg['Value']['Min views period'])
print('Average views during the selected period:', pageviews_agg['Value']['Average views'])_____no_output_____

---

# D. Templates and Protection

Provided through the WikiWho API._____no_output_____

def template_button(b):
    global token_source
    global new_template
    global new_protect
    global token_source
    with template_out:
        clear_output()

        # WikiWho API.
        wikiwho = WikiWho(lng=abbreviation(languageSelection.value))
        display(md("Downloading all_content from the WikiWhoApi..."))
        content = wikiwho.dv.all_content(the_page['page_id'])
        display(md("Downloading revisions from the WikiWhoApi..."))
        revisions = wikiwho.dv.rev_ids_of_article(the_page['page_id'])
        clear_output()

        # Wikipedia API
        pp_log = wikipedia_dv.get_protection(the_page['title'])

        # Use ConflictManager to join content and revision tables.
        cm = ConflictManager(content,
                             revisions,
                             lng=abbreviation(languageSelection.value),
                             include_stopwords=True)
        cm.calculate()
        clear_output()
        token_source = cm.all_actions.copy()

        display(md(f"***Page: {the_page['title']} ({abbreviation(languageSelection.value).upper()})***"))

        # Templateslistener
        new_protect = ProtectListener(pp_log, lng=abbreviation(languageSelection.value))

        display(md("Analysing protection data..."))
        plot_protect = [new_protect.get_protect(level)[1] for level in ["semi_edit", "semi_move", "fully_edit", "fully_move", "unknown"]]
        plot_protect = pd.concat(plot_protect)

        new_template = TemplateListener(token_source, plot_protect, lng=abbreviation(languageSelection.value),
                                       wikipediadv_api=wikipedia_dv, page=the_page)
        new_template.listen()

# create and display the button
button = widgets.Button(description="Get Templates & Protection", layout=Layout(width='200px'))
display(button)

# the output widget is used to remove the output after the search field
template_out = Output()
display(template_out)

# set the event
button.on_click(template_button)

# trigger the event with the default value
template_button(button)_____no_output_____

After we have no seen some general statistics of the article and the views it attracted, we will go on to take a look at what specific kinds of changes by which editors it was subject to over time.

Click below to go to the next notebook. You can later come back to this notebook and simply enter another article name to start the process over with that new article. _____no_output_____[Go to next workbook](./2_Page_analysis.ipynb)_____no_output_____

# from utils.notebooks import get_next_notebook
# display(HTML(f'Go to next workbook'))

re_hide = """

document.getElementById('notebook').scrollIntoView();
</script
"""
display(HTML(scroll_to_top))_____no_output_____


# Notebook from adriana1304/pyristic
Path: examples/SpanishExamples/pruebaEE.ipynb

# Estrategias Evolutivas

La librería **Pyristic** incluye una clase llamada *EvolutionStrategy*  inspirada en la metaheurística de *Estrategias evolutivas* (EE) para resolver problemas de minimización. Para trabajar con esta clase se requiere hacer lo siguiente:

1. Definir:
    * La función objetivo $f$.
    * La lista de restricciones.
    * Lista de límites inferiores y superiores.
    * Configuración de operadores de la metaheurística (opcional).

2. Crear una clase que hereda de *EvolutionStrategy*.

3. Sobreescritura de funciones auxiliares:

    * initialize_step_weights (opcional)
    * initialize_population (opcional)
    * fixer (opcional)
    * mutation_operator (opcional)
    * crossover_operator (opcional)
    * adaptive_crossover (opcional)
    * adaptive_mutation (opcional)
    * survivor_selection (opcional)

A continuación se mostrarán los elementos que se deben importar. _____no_output_____

import sys
import os

#library_path is the path where the Optimpy library is located.
library_path = "/home/dell/Documentos/Git_proejcts/optimizacion-con-metaheuristicas/"
#library_path = "/Users/adrianamenchacamendez/Documentos/enes_morelia/papime/optimizacion-con-metaheuristicas/"
sys.path.append(os.path.abspath(library_path))

_____no_output_____from pprint import pprint
import math
import numpy as np
import copy
from IPython.display import Image
from IPython.core.display import HTML _____no_output_____from optimpy.heuristic.EvolutionStrategy_search import EvolutionStrategy
from optimpy.utils.operators import selection, mutation, crossover
from optimpy.utils.test_function import beale_, ackley_
from optimpy.utils.helpers import EvolutionStrategyConfig,get_stats
_____no_output_____

Para ejemplificar el uso de la clase *EvolutionStrategy*,  resolveremos dos problemas de optimización continua. Estos problemas se encuentran implementados en **utils.helpers.test_function**.
_____no_output_____## Función de Beale
\begin{equation}
  \label{eq:BF}
  \begin{array}{rll}
  \text{minimizar:} & f(x_1, x_2) = (1.5 - x_1 + x_1x_2)^2 + (2.25 - x_1 + x_1x_2^2)^2 + (2.625 - x_1 + x_1x_2^3)^2
  &  \\
  \text{Tal que: } & -4.5 \leq x_1,x_2 \leq 4.5 &
  \end{array}
\end{equation}

El mínimo global se encuentra en $x^* = (3, 0.5)$ y $f(x^*) = 0$.
_____no_output_____

Image(filename="include/beale.png", width=500, height=300)_____no_output_____beale______no_output_____

### Clase EvolutionStrategy
Para hacer uso de la metaheurística EE implementada en la librería de **Pyristic** se puede hacer lo siguiente.

* Crear un objeto de tipo *EvolutionStrategy* y mandar a llamar a su método *optimize*. En este caso se utilizan los operadores y parámetros por defecto.
* Crear una clase que herede de la clase *EvolutionStrategy* y sobreescribir los métodos.
* Declarar un objeto del tipo *EvolutionStrategyConfig* (ubicado en *utils.helpers*) y ajustar los operadores.
* Realizar una combinación de las dos antes mencionadas._____no_output_____#### Métodos
Los métodos que contiene la clase EvolutionStrategy son: _____no_output_____
* **fixer**. Si la solución no está dentro de los límites (restricciones de caja), actualiza el valor de la variable con el valor del límite que rebasó. De lo contrario, regresa la misma solución.

 Argumentos:
  * **ind**. Índice del individuo.

 Valor de retorno:
  * Un arreglo de *numpy* representado a la solución que reemplazará la solución infactible._____no_output_____* **initialize_population**. Crea una población de individuos aleatorios. Para ello se generan números aleatorios, utilizando una distribución uniforme, dentro de los límites indicados para cada variable. Los individuos generados son almacenados en logger con la llave de `parent_population_x`. Esta función es llamada dentro de la función *optimize*.

 Valor de retorno:
   * Un arreglo bidimensional de *numpy*. El número de filas es igual al tamaño de la población y el número de columnas es igual al número de variables que tiene el problema que se está resolviendo._____no_output_____* **initialize_step_weights.** Inicializa el tamaño de desplazamiento de cada una de las variables de decisión pertenecientes a cada individuo de la población. Para ello se generan números aleatorio en el intervalo $[0,1]$, utilizando una distribución uniforme. Los tamaños de desplazamiento están almacenados en logger con la llave `parent_population_sigma`.

    Valor de retorno:
     * Un arreglo bidimensional de numpy. El número de filas es igual al tamaño de la población y el número de columnas es igual al número de variables que tiene el problema que se está resolviendo. Cada variable tiene su propio tamaño de paso.

_____no_output_____* **mutation_operator**, Muta las variables de decisión, las cuales se encuentran almacenadas en el diccionario logger con la llave `parent_population_x`. La mutación se realiza de la siguiente forma:

    \begin{equation}
      \label{eq:mutarVariables}
      x'_i = x_i + \sigma'_i \cdot N(0, 1)
    \end{equation}

  donde $x'_j$ es la variable mutada, $x_j$ la variable a mutar, $\sigma'_j$ el tamaño de paso (previamente mutado) y N(0,1) devuelve un número aleatorio usando una distribución normal con media $0$ y desviación estándar igual con $1$.

  Es importante tener en cuenta que la fila $j$ de `parent_population_x` debe corresponder con la fila $j$ de `parent_population_sigma`.

  Valor de retorno:
   * Un arreglo bidimensional de numpy representado a los nuevos individuos.

_____no_output_____* **adaptive_mutation.**  Muta los tamaños de paso, los cuales se encuentran almacenados en el diccionario logger con la llave `offspring_population_sigma`. Actualmente, la librería **Pyristic** cuenta con dos métodos para la actualización de los tamaños de paso, los métodos se encuentran en *utils.operators.mutation*.

  Los métodos de mutación para sigma son:
   * *sigma_mult_adaptive_mutator.* Crea una variable de desplazamiento por cada variable de decisión de cada individuo.
   * *sigma_single_adaptive_mutator.* Crea una sola variable de desplazamiento por cada individuo de la población.

 Por defecto la metaheurística utiliza *sigma_mult_adaptive_mutator*, la forma en que opera es:
    \begin{equation*}
      \label{eq:mutarNSigmas}
      \sigma'_i = \sigma_i \cdot e ^ {\tau' \cdot N(0,1) + \tau \cdot N_i(0,1)}
    \end{equation*}
_____no_output_____* **crossover_operator.** Genera la población de nuevos individuos trás la cruza de cada dos inviduos de la población en dicha generación. Estos nuevos individuos son almacenandos en logger con la llave `offspring_population_x`.

    Argumentos:
     * **parent_ind1.** Indices de los individuos que son seleccionados para emplear el operador de cruza.
     * **parent_ind2.** Indices de los individuos que son seleccionados para emplear el operador de cruza.

 Valor de retorno:
     * Un arreglo bidimensional de *numpy* representado a los nuevos individuos.

  Los inviduos de *parent_ind1* y *parent_ind2* son emparejados, es decir, se toman los padres en la posición $i$ en los dos arreglos y dichos padres son cruzados.

  Por defecto la metaheurística utiliza el operador de cruza discreta que se encuentra en *utils.operators.crossover* con el nombre de *discrete*.
  _____no_output_____* **adaptive_crossover.** Genera una cruza de los tamaños de paso de los individuos seleccionados de la población. Estos nuevos tamaños de paso son almacenados en logger con la llave `offspring_population_sigma`.

 Argumentos:
 * **parent_ind1.** Indices de los individuos que son seleccionados para emplear el operador de cruza.
 * **parent_ind2.** Indices de los individuos que son seleccionados para emplear el operador de cruza.

 Valor de retorno:
 * Un arreglo bidimensional de *numpy* representando los nuevos tamaños de paso.

  Por defecto la metaheurística utiliza el operador que se encuentra en *utils.operators.crossover* con el nombre de *intermediate*._____no_output_____* **survivor_selection.** Selección de los individuos que pasarán a la próxima generación. la librería cuenta con dos esquemas de selección implementados en *utils.operators.selection*. Los esquemas de selección son:
    * **merge_selector.** Esquema $(\mu + \lambda)$, selecciona $\mu$ individuos que son obtenidos al unir la población de individuos creados por la cruza y la población actual. Los  individuos que permanecerán para la próxima generación son los más *aptos* de acuerdo a su función objetivo.
    * **replacement_selector.** El esquema $(\mu, \lambda)$, reemplaza la población actual con $\mu$ individuos de la población obtenida por la cruza. Los nuevos individuos son seleccionados por su función objetivo.

  Valor de retorno:
    * Una lista con un arreglo bidimensional de *numpy* representando la población $\mu$ y un arreglo *numpy* que será los valores de la función objetivo correspondientes de los individuos de la población $\mu$.

  Por defecto la metaheurística utiliza el esquema $(\mu + \lambda)$._____no_output_____## Ejecución de la metaheurística
Creamos una instancia del tipo EvolutionStrategy indicando en los parámetros la función objetivo, las restricciones del problema, los limites del espacio de búsqueda por cada una de las variables de decisión y el número de variables de decisión.
_____no_output_____

Beale = EvolutionStrategy(**beale_)_____no_output_____

Finalmente, se llama a la función optimize (esta función es la misma para todas las clases en la librería). La función optimize recibe los siguientes parámetros:
   * **generations.**  Número de generaciones (iteraciones de la metaheurística).
   * **population_size.** Tamaño de la población (número de individuos).
   * **offspring_size.** Tamaño de la población creada por la cruza.
   * **eps_sigma.** Número flotante, indica lo menos que puede ser por tamaño de paso, por defecto está en 0.001.
   * **verbose.**  Muestra en que iteración se encuentra nuestra búsqueda, por defecto está en True.
   * **kargs: diccionario con parámetros adicionales que queremos incluir en la búsqueda.

Para esta ejecución para la función de Beale utilizaremos los siguientes parámetros:

   * **generations** = $300$
   * **population_size** = 80
   * **offspring_size** = 160

_____no_output_____### ($\mu + \lambda$) - EE
Por defecto, la metaheurística realiza:
* La selección de sobrevivientes, utiliza un esquema $(\mu + \lambda)$
* El operador de cruza  para las variables de decisión es *recombinación discreta*.
* El operador de cruza para los tamaños de paso es *recombinación intermedia*.
_____no_output_____

Beale.optimize(3,3,6,verbose=True)100%|██████████| 3/3 [00:01<00:00,  1.70it/s]
print(Beale)Evolution Strategy search:
 f(X) = 0.10955969930560476
 X = [4.5        0.70923685]
 Constraints:
 x1: -4.5 <= 4.50 <= 4.5
 x2: -4.5 <= 0.71 <= 4.5

Beale.logger_____no_output_____

Para revisar el comportamiento de la metaheurística en determinado problema, la librería **Pyristic** cuenta con una función llamada get_stats. Esta función se encuentra en utils.helpers y recibe como parámetros:

   * Objeto que realiza la búsqueda de soluciones.
   * El número de veces que se quiere ejecutar la metaheurística.
   * Los argumentos que recibe la función optimize (debe ser una tupla).
   * Argumentos adicionales a la búsqueda(opcional).

La función **get_stats** retorna un diccionario con algunas estadísticas de las ejecuciones.

_____no_output_____

args = (200, 100, 150)
statistics = get_stats(Beale, 30, args)100%|██████████| 200/200 [00:00<00:00, 642.61it/s]
100%|██████████| 200/200 [00:00<00:00, 1005.35it/s]
100%|██████████| 200/200 [00:00<00:00, 1016.00it/s]
100%|██████████| 200/200 [00:00<00:00, 962.01it/s]
100%|██████████| 200/200 [00:00<00:00, 960.26it/s]
100%|██████████| 200/200 [00:00<00:00, 1020.20it/s]
100%|██████████| 200/200 [00:00<00:00, 968.41it/s]
100%|██████████| 200/200 [00:00<00:00, 948.27it/s]
100%|██████████| 200/200 [00:00<00:00, 878.73it/s]
100%|██████████| 200/200 [00:00<00:00, 944.59it/s]
100%|██████████| 200/200 [00:00<00:00, 797.91it/s]
100%|██████████| 200/200 [00:00<00:00, 1035.07it/s]
100%|██████████| 200/200 [00:00<00:00, 855.56it/s]
100%|██████████| 200/200 [00:00<00:00, 957.49it/s]
100%|██████████| 200/200 [00:00<00:00, 770.00it/s]
100%|██████████| 200/200 [00:00<00:00, 815.71it/s]
100%|██████████| 200/200 [00:00<00:00, 976.47it/s]
100%|██████████| 200/200 [00:00<00:00, 774.70it/s]
100%|██████████| 200/200 [00:00<00:00, 867.17it/s]
100%|██████████| 200/200 [00:00<00:00, 1019.11it/s]
100%|██████████| 200/200 [00:00<00:00, 853.13it/s]
100%|██████████| 200/200 [00:00<00:00, 944.85it/s]
100%|██████████| 200/200 [00:00<00:00, 977.25it/s]
100%|██████████| 200/200 [00:00<00:00, 939.14it/s]
100%|██████████| 200/200 [00:00<00:00, 972.77it/s]
100%|██████████| 200/200 [00:00<00:00, 1001.79it/s]
100%|██████████| 200/200 [00:00<00:00, 1008.56it/s]
100%|██████████| 200/200 [00:00<00:00, 1005.72it/s]
100%|██████████| 200/200 [00:00<00:00, 967.20it/s]
100%|██████████| 200/200 [00:00<00:00, 1012.96it/s]
pprint(statistics){'Best solution': {'f': 2.531750467693685e-29, 'x': array([3. , 0.5])},
 'Mean': 7.167292303379842e-23,
 'Standard deviation': 2.7240425061399494e-22,
 'Worst solution': {'f': 1.5028900595968608e-21, 'x': array([3. , 0.5])}}

### (1+1) - EE
Para este ejemplo resolveremos este problema utilizando la versión más simple de un algoritmo de estrategias evolutivas.

La búsqueda local se desarrolla de la siguiente manera:
 1. Mutamos el individuo con un parámetro ($\sigma$) de adaptación independiente al número de variables de decisión que tenga el problema.
 2. Mutamos $\sigma$ del nuevo individuo utilizando la regla del éxito 1/5.
 3. Se selecciona el individuo más apto que pasará a la próxima generación, es decir, se seleccionará al individuo que tenga un valor más pequeño en la función objetivo.

La regla del éxito 1/5, actualiza el parámetro de la siguiente manera:
\begin{equation}
  \sigma =
  \begin{cases}
   \sigma / c & \text{Si } p_s > 1/5, \\
   \sigma \cdot c & \text{Si } p_s < 1/5, \\
   \sigma  & \text{Si } p_s = 1/5 \\
  \end{cases}
\end{equation}

La forma en que mutamos el nuevo individuo se realiza así:
    \begin{equation}
      \label{eq:mutarVariables}
      x'_i = x_i + \sigma'_i \cdot N(0, 1)
    \end{equation}

Es importante resaltar, que esta versión no considera el operador de cruza (*crossover_operator* y *adaptive_crossover*). La razón es porque el operador está condicionado a seleccionar dos individuos de la población para generar 2 nuevas soluciones.

Vamos a sobreescribir *crossover_operator* y *adaptive_crossover* para que retornen el individuo original sin cambio alguno. Las funciones que serán alteradas en su comportamiento son:
* adaptive_mutation, se encarga de modificar $\sigma$.
* mutation_operator, esta función hará un pequeño cambio a la solución original tomando $\sigma$.
* initializing_step_weights, Inicializar el conjunto de $\sigma$._____no_output_____

beale______no_output_____class ESBasic(EvolutionStrategy):

    def __init__(self, function  ,\
                 decision_variables:int,\
                 constraints: list,\
                 bounds: np.ndarray):
        super().__init__(function, decision_variables, constraints, bounds)

        self.successful = 0

    def crossover_operator(self, parent_ind1,\
                                 parent_ind2,\
                                 **kargs):
        return self.logger['parent_population_x']

    def adaptive_crossover(self, parent_ind1,\
                                 parent_ind2,\
                                 **kargs):
        return self.logger['parent_population_sigma']

    def adaptive_mutation(self, **kargs):

            if self.logger['current_iter'] % kargs['k'] != 0:
                return self.logger['offspring_population_sigma']

            ps = self.successful / kargs['k']
            self.successful = 0
            if( ps > 1/5):
                return self.logger['offspring_population_sigma']/kargs['c']
            elif( ps < 1/5):
                return self.logger['offspring_population_sigma']*kargs['c']
            else:
                return self.logger['offspring_population_sigma']

    def mutation_operator(self, **kargs):
        X_mutated = copy.deepcopy(self.logger['offspring_population_x'])
        X_mutated += self.logger['offspring_population_sigma'] * np.random.normal(0,1, size=X_mutated.shape)

        if(self.f(self.logger['offspring_population_x'][0]) > self.f(X_mutated[0])):
            self.successful+=1

        return X_mutated

    def initializing_step_weights(self, eps_sigma:int, **kwargs) -> np.ndarray:
        return np.random.uniform(0,1, size=(self.logger['parent_population_size'], 1))_____no_output_____Beale_basic = ESBasic(**beale_)_____no_output_____

En la implementación notamos que estamos haciendo uso de un argumento declarado *kargs*, dicho argumento es un diccionario que contiene información adicional a la búsqueda. En este caso contiene dos constantes que podemos modificar antes de llamar la función **EEClass.optimize**.

El diccionario tiene que ser incluido siempre con `**` antes del nombre de la variable que contiene este diccionario._____no_output_____

additional_arguments = {'k':10,'c':0.83}_____no_output_____Beale_basic.optimize(generations=100,\
                     population_size=1,\
                     offspring_size=1,\
                     **additional_arguments)100%|██████████| 100/100 [00:00<00:00, 3951.26it/s]
print(Beale_basic)Evolution Strategy search:
 f(X) = 0.8617989584848492
 X = [-3.48781252  1.22350314]
 Constraints:
 x1: -4.5 <= -3.49 <= 4.5
 x2: -4.5 <= 1.22 <= 4.5

args = (500, 1, 1, True)
statistics = get_stats(Beale_basic, 30, args,**additional_arguments )100%|██████████| 500/500 [00:00<00:00, 4175.56it/s]
100%|██████████| 500/500 [00:00<00:00, 6980.18it/s]
100%|██████████| 500/500 [00:00<00:00, 16295.65it/s]
100%|██████████| 500/500 [00:00<00:00, 14609.11it/s]
100%|██████████| 500/500 [00:00<00:00, 13795.33it/s]
100%|██████████| 500/500 [00:00<00:00, 14063.90it/s]
100%|██████████| 500/500 [00:00<00:00, 14416.49it/s]
100%|██████████| 500/500 [00:00<00:00, 11590.57it/s]
100%|██████████| 500/500 [00:00<00:00, 14894.12it/s]
100%|██████████| 500/500 [00:00<00:00, 15258.23it/s]
100%|██████████| 500/500 [00:00<00:00, 14794.10it/s]
100%|██████████| 500/500 [00:00<00:00, 12553.21it/s]
100%|██████████| 500/500 [00:00<00:00, 14944.43it/s]
100%|██████████| 500/500 [00:00<00:00, 13289.52it/s]
100%|██████████| 500/500 [00:00<00:00, 12975.66it/s]
100%|██████████| 500/500 [00:00<00:00, 13600.21it/s]
100%|██████████| 500/500 [00:00<00:00, 15335.10it/s]
100%|██████████| 500/500 [00:00<00:00, 15661.96it/s]
100%|██████████| 500/500 [00:00<00:00, 13863.18it/s]
100%|██████████| 500/500 [00:00<00:00, 11878.85it/s]
100%|██████████| 500/500 [00:00<00:00, 10007.79it/s]
100%|██████████| 500/500 [00:00<00:00, 13039.31it/s]
100%|██████████| 500/500 [00:00<00:00, 14481.99it/s]
100%|██████████| 500/500 [00:00<00:00, 16446.32it/s]
100%|██████████| 500/500 [00:00<00:00, 16050.33it/s]
100%|██████████| 500/500 [00:00<00:00, 16198.23it/s]
100%|██████████| 500/500 [00:00<00:00, 15697.13it/s]
100%|██████████| 500/500 [00:00<00:00, 15791.81it/s]
100%|██████████| 500/500 [00:00<00:00, 13883.73it/s]
100%|██████████| 500/500 [00:00<00:00, 14785.96it/s]
pprint(statistics){'Best solution': {'f': 6.046204079720323e-07,
                   'x': array([2.99808282, 0.49949865])},
 'Mean': 0.2718825230311985,
 'Standard deviation': 0.37798082885593254,
 'Worst solution': {'f': 0.921247769181009,
                    'x': array([-3.27730716,  1.22975648])}}

## Función de Ackley

\begin{equation}
  \min f(\vec{x}) = -20\exp \left( -0.2 \sqrt{\frac{1}{n} \sum_{i=1}^n x_i^2} \right)
  - exp \left( \frac{1}{n} \sum_{i=1}^n \cos (2\pi x_i) \right)
  + 20 + e
\end{equation}

El mínimo global está en $x^* = 0 $ y $f(\vec{x}) = 0$._____no_output_____

Image(filename="include/ackley.jpg", width=500, height=300)_____no_output_____ackley_['decision_variables']=4_____no_output_____

### Descripción
Ahora vamos a mostrar la forma de implementar una instancia de *EvolutionStrategy*, pero utilizando un constructor con los operadores que deseamos utilizar. A continuación explicaremos los siguientes operadores populares en el uso de estrategias evolutivas.

Operadores de cruza:
   * discrete_crossover.
   * intermediate_crossover.

Operadores de mutación:
   * sigma_single_adaptive.
   * sigma_mult_adaptive.

_____no_output_____#### discrete_crossover
toma como parámetros de entrada:
* Una matriz $X$ de numpy, donde, cada fila es un individuo de nuestra población y el número de columnas es el número de variables de decisión.
* Indices seleccionados como primer padre.
* Indices seleccionados como segundo padre.

Combina dos individuos de la siguiente manera:
\begin{equation}
  z_{i} =
  \begin{cases}
   x_{i} & \text{Si } b_{i} = 1, \\
   y_{i}  & \text{Si } b_{i} = 0 \\
  \end{cases}
\end{equation}_____no_output_____#### intermediate_crossover
toma como parámetros de entrada:
* Una matrix $X$ de numpy, donde, cada fila es un individuo de nuestra población y el número de columnas es el número de variables de decisión.
* Un arreglo con los indices seleccionados como primer padre.
* Un arreglo con los indices seleccionados como segundo padre.
Combina dos individuos de la siguiente manera:
\begin{equation}
z_{i} = \frac{x_{i} + y_{i}}{2}
\end{equation}_____no_output_____#### sigma_single_adaptive
toma como parámetros de entrada:
* Una matriz $X$ de numpy, donde, cada fila es un individuo de nuestra población y el número de columnas es el número de variables de decisión.
* Un valor real que será $\tau$.

\begin{equation}
 \sigma' = \sigma \cdot e^{\tau \cdot N(0,1)}
\end{equation}_____no_output_____####  sigma_mult_adaptive
toma como parámetros de entrada:
* Una matriz $X$ de numpy, donde, cada fila es un individuo de nuestra población y el número de columnas es el número de variables de decisión.
* Valor numérico real $\tau'$.
* Valor numérico real $\tau$.

\begin{equation}
\sigma'_i = \sigma_i \cdot e ^ {\tau' \cdot N(0,1) + \tau \cdot N_i(0,1)}
\end{equation}_____no_output_____### Instancia de Programación Evolutiva¶

Para esta ejecución para la función de Ackley utilizaremos los siguientes parámetros:

   * **generations** = $250$
   * **population_size** = $100$
   * **offspring_size** = $200$

_____no_output_____### ($\mu+\lambda$)-EE
_____no_output_____En este caso dejaremos todos los demás operadores por defecto, hemos modificado son el de cruza y mutación._____no_output_____

Ackley = EvolutionStrategy(**ackley_)_____no_output_____Ackley.optimize(1,3,6)100%|██████████| 1/1 [00:00<00:00,  5.38it/s]
print(Ackley)Evolution Strategy search:
 f(X) = 19.476948083887855
 X = [ 8.96934925  5.09846682 18.70513223 -7.57257029]
 Constraints:
 x1: -30 <= 8.97 <= 30
 x2: -30 <= 5.10 <= 30
 x3: -30 <= 18.71 <= 30
 x4: -30 <= -7.57 <= 30

Ackley.logger_____no_output_____Ackley.survivor_selection()_____no_output_____Ackley.logger['parent_population_sigma'][0]_____no_output_____args = (250, 100, 200)
statistics = get_stats(Ackley, 30, args)100%|██████████| 250/250 [00:01<00:00, 240.56it/s]
100%|██████████| 250/250 [00:00<00:00, 284.03it/s]
100%|██████████| 250/250 [00:00<00:00, 309.16it/s]
100%|██████████| 250/250 [00:00<00:00, 293.80it/s]
100%|██████████| 250/250 [00:00<00:00, 280.59it/s]
100%|██████████| 250/250 [00:00<00:00, 278.84it/s]
100%|██████████| 250/250 [00:00<00:00, 285.39it/s]
100%|██████████| 250/250 [00:00<00:00, 290.13it/s]
100%|██████████| 250/250 [00:00<00:00, 293.06it/s]
100%|██████████| 250/250 [00:00<00:00, 304.24it/s]
100%|██████████| 250/250 [00:00<00:00, 291.28it/s]
100%|██████████| 250/250 [00:00<00:00, 284.69it/s]
100%|██████████| 250/250 [00:00<00:00, 298.78it/s]
100%|██████████| 250/250 [00:00<00:00, 302.24it/s]
100%|██████████| 250/250 [00:00<00:00, 285.02it/s]
100%|██████████| 250/250 [00:00<00:00, 270.83it/s]
100%|██████████| 250/250 [00:00<00:00, 283.50it/s]
100%|██████████| 250/250 [00:00<00:00, 316.32it/s]
100%|██████████| 250/250 [00:00<00:00, 311.13it/s]
100%|██████████| 250/250 [00:00<00:00, 286.08it/s]
100%|██████████| 250/250 [00:00<00:00, 288.20it/s]
100%|██████████| 250/250 [00:00<00:00, 275.65it/s]
100%|██████████| 250/250 [00:00<00:00, 302.16it/s]
100%|██████████| 250/250 [00:00<00:00, 295.19it/s]
100%|██████████| 250/250 [00:00<00:00, 295.26it/s]
100%|██████████| 250/250 [00:00<00:00, 290.50it/s]
100%|██████████| 250/250 [00:00<00:00, 271.67it/s]
100%|██████████| 250/250 [00:00<00:00, 296.29it/s]
100%|██████████| 250/250 [00:00<00:00, 291.37it/s]
100%|██████████| 250/250 [00:00<00:00, 298.67it/s]
pprint(statistics){'Best solution': {'f': 3.9968028886505635e-15,
                   'x': array([-2.08159927e-15, -1.46190110e-16,  3.20354101e-16, -4.42506442e-16])},
 'Mean': 2.1743495892678765e-12,
 'Standard deviation': 7.788381322703422e-12,
 'Worst solution': {'f': 4.224931515750541e-11,
                    'x': array([2.33953833e-12, 8.14628091e-12, 1.92327519e-11, 2.12383507e-12])}}

### ($\mu,\lambda$)-EE_____no_output_____El constructor de la configuración de los operadores de estrategias evolutivas (EvolutionStrategyConfig) es indicando los operadores a modificar:
* .cross( *operador de cruza* )
* .mutate( *operador de mutación* )
* .survivor_selection( *esquema de selección* )
* .fixer_invalide_solutions( *función para retornar un individuo invalido*)
* .adaptive_crossover( *operador de cruza para $\sigma$* )
* .adaptive_mutation( *operador de mutación para $\sigma$*)_____no_output_____

configuration_ackley = (EvolutionStrategyConfig()
                       .survivor_selection(selection.replacement_selector())
                       )_____no_output_____print(configuration_ackley)--------------------------------
	Configuration
--------------------------------
Survivor selection: Replacement population

--------------------------------
solver_ackley_custom = EvolutionStrategy(**ackley_,config=configuration_ackley)_____no_output_____solver_ackley_custom.optimize(150,100,200)100%|██████████| 150/150 [00:00<00:00, 244.74it/s]
solver_ackley_custom.logger_____no_output_____print(solver_ackley_custom)Evolution Strategy search:
 f(X) = 0.0004422434596915714
 X = [-1.26118584e-04 -1.26475657e-04 -1.29772722e-04 -2.90680402e-06]
 Constraints:
 x1: -30 <= -0.00 <= 30
 x2: -30 <= -0.00 <= 30
 x3: -30 <= -0.00 <= 30
 x4: -30 <= -0.00 <= 30

args = (250, 100, 200)
statistics = get_stats(solver_ackley_custom, 30, args)100%|██████████| 250/250 [00:00<00:00, 268.24it/s]
100%|██████████| 250/250 [00:01<00:00, 246.21it/s]
100%|██████████| 250/250 [00:01<00:00, 233.22it/s]
100%|██████████| 250/250 [00:01<00:00, 232.30it/s]
100%|██████████| 250/250 [00:01<00:00, 227.49it/s]
100%|██████████| 250/250 [00:00<00:00, 281.50it/s]
100%|██████████| 250/250 [00:01<00:00, 231.45it/s]
100%|██████████| 250/250 [00:01<00:00, 234.93it/s]
100%|██████████| 250/250 [00:00<00:00, 289.59it/s]
100%|██████████| 250/250 [00:01<00:00, 238.75it/s]
100%|██████████| 250/250 [00:01<00:00, 247.36it/s]
100%|██████████| 250/250 [00:00<00:00, 311.85it/s]
100%|██████████| 250/250 [00:01<00:00, 240.00it/s]
100%|██████████| 250/250 [00:00<00:00, 316.78it/s]
100%|██████████| 250/250 [00:00<00:00, 255.01it/s]
100%|██████████| 250/250 [00:00<00:00, 276.98it/s]
100%|██████████| 250/250 [00:00<00:00, 250.73it/s]
100%|██████████| 250/250 [00:00<00:00, 278.50it/s]
100%|██████████| 250/250 [00:01<00:00, 221.41it/s]
100%|██████████| 250/250 [00:00<00:00, 280.72it/s]
100%|██████████| 250/250 [00:01<00:00, 232.29it/s]
100%|██████████| 250/250 [00:00<00:00, 296.03it/s]
100%|██████████| 250/250 [00:01<00:00, 242.21it/s]
100%|██████████| 250/250 [00:01<00:00, 245.74it/s]
100%|██████████| 250/250 [00:00<00:00, 284.07it/s]
100%|██████████| 250/250 [00:01<00:00, 239.76it/s]
100%|██████████| 250/250 [00:00<00:00, 268.19it/s]
100%|██████████| 250/250 [00:00<00:00, 258.01it/s]
100%|██████████| 250/250 [00:01<00:00, 232.09it/s]
100%|██████████| 250/250 [00:00<00:00, 283.07it/s]
pprint(statistics){'Best solution': {'f': 8.964739278427558e-05,
                   'x': array([-7.42417909e-06,  4.26983588e-05,  9.73961424e-06, -5.90230805e-06])},
 'Mean': 14.716142821235808,
 'Standard deviation': 8.684184847870295,
 'Worst solution': {'f': 19.950424956466673,
                    'x': array([ 30., -30., -30., -30.])}}

## (1+1)-EE_____no_output_____

Ackley_basic = ESBasic(**ackley_)_____no_output_____Ackley_basic.optimize(10000,1,1,**additional_arguments )100%|██████████| 10000/10000 [00:00<00:00, 12505.39it/s]
Ackley_basic.logger_____no_output_____print(Ackley_basic)Evolution Strategy search:
 f(X) = 17.564532106624274
 X = [16.99260666 -1.00779619 -3.01160645 11.99402034]
 Constraints:
 x1: -30 <= 16.99 <= 30
 x2: -30 <= -1.01 <= 30
 x3: -30 <= -3.01 <= 30
 x4: -30 <= 11.99 <= 30

args = (10000, 1, 1)
statistics = get_stats(Ackley_basic, 30, args,**additional_arguments )100%|██████████| 10000/10000 [00:00<00:00, 11541.06it/s]
100%|██████████| 10000/10000 [00:00<00:00, 13418.02it/s]
100%|██████████| 10000/10000 [00:00<00:00, 12357.89it/s]
100%|██████████| 10000/10000 [00:00<00:00, 13047.87it/s]
100%|██████████| 10000/10000 [00:00<00:00, 14063.26it/s]
100%|██████████| 10000/10000 [00:00<00:00, 13282.79it/s]
100%|██████████| 10000/10000 [00:00<00:00, 12737.60it/s]
100%|██████████| 10000/10000 [00:00<00:00, 13067.66it/s]
100%|██████████| 10000/10000 [00:00<00:00, 12395.00it/s]
100%|██████████| 10000/10000 [00:00<00:00, 12825.49it/s]
100%|██████████| 10000/10000 [00:00<00:00, 13392.70it/s]
100%|██████████| 10000/10000 [00:00<00:00, 13341.01it/s]
100%|██████████| 10000/10000 [00:00<00:00, 12864.50it/s]
100%|██████████| 10000/10000 [00:00<00:00, 13764.00it/s]
100%|██████████| 10000/10000 [00:00<00:00, 12022.56it/s]
100%|██████████| 10000/10000 [00:00<00:00, 13500.36it/s]
100%|██████████| 10000/10000 [00:00<00:00, 13829.84it/s]
100%|██████████| 10000/10000 [00:00<00:00, 12884.88it/s]
100%|██████████| 10000/10000 [00:00<00:00, 13480.66it/s]
100%|██████████| 10000/10000 [00:00<00:00, 14059.45it/s]
100%|██████████| 10000/10000 [00:00<00:00, 12991.70it/s]
100%|██████████| 10000/10000 [00:00<00:00, 13543.55it/s]
100%|██████████| 10000/10000 [00:00<00:00, 13492.47it/s]
100%|██████████| 10000/10000 [00:00<00:00, 12831.78it/s]
100%|██████████| 10000/10000 [00:00<00:00, 13698.93it/s]
100%|██████████| 10000/10000 [00:00<00:00, 12930.61it/s]
100%|██████████| 10000/10000 [00:00<00:00, 12941.49it/s]
100%|██████████| 10000/10000 [00:00<00:00, 12997.27it/s]
100%|██████████| 10000/10000 [00:00<00:00, 12958.73it/s]
100%|██████████| 10000/10000 [00:00<00:00, 13241.25it/s]
pprint(statistics){'Best solution': {'f': 1.8539269092102804,
                   'x': array([ 0.91418066,  0.00244596,  0.00664822, -0.02842378])},
 'Mean': 18.15852196704732,
 'Standard deviation': 3.6212730772745068,
 'Worst solution': {'f': 19.935311441075847,
                    'x': array([ 30.        ,  30.        , -25.00184492, -29.00929935])}}