```python
from IPython.core.display import HTML, Image, Markdown
import sys
import json
sys.path.append("microTC")
from microtc.textmodel import TextModel
from itertools import combinations

from graphviz import Digraph
dot = Digraph(comment='microtc pipeline', format="png")
# dot.engine = 'circo'
dot.graph_attr['rankdir'] = 'LR'
dot.node('i', '', style="invis")
dot.node('n', 'Normalización')
dot.node('t', 'Tokenización')
dot.node('w', 'Pesado')
dot.node('c', 'Clasificación')
dot.node('o', '', style="invis")

dot.edge('i', 'n', label="texto entrada")
dot.edge('n', 't', label="texto normalizado")
dot.edge('t', 'w', label="bolsa de palabras")
dot.edge('w', 'c', label="vector con pesos")
dot.edge('c', 'o', label="clase")

pipeline = dot.render("fig-pipeline", view=False)

```

# Clasificación de texto #
## Un enfoque basado en $\mu TC$ ##

**Seminario de la Sociedad Matemática Mexicana SMM 2016**

    Eric Sadit Téllez Avila INFOTEC
    <estellezav@conacyt.mx>

       CONACyT -- INFOTEC

# Agenda #
- ¿Qué es $\mu TC$
- ¿En qué consiste la tarea de clasificación de texto?
- ¿Cómo esta compuesto $\mu TC$?
- Estado del arte
- Cómo se compara $\mu TC$ con el estado del arte
- Qué falta en $\mu TC$
- Ejemplos

## Categorización de texto ##
El problema consiste en, dado un texto $d$, determinar la(s) categoría(s) a la que pertenece en un conjunto $C$ de categorias, previamente conocido.

Más formalmente:

Dado un conjunto de categorias $\cal{C} = \{c_1, ..., c_m\}$, determinar el subconjunto de categorias
$C_d \in \wp(\cal{C})$ a las que pertenece $d$.

Notese que $C_t$ puede ser vacio o $\cal{C}$.

## Clasificación de texto ##
La _clasificación_ de texto es una especialización del problema de categorización, donde $|C_d| = 1$, esto es $d$ solo puede ser asignado a una categoría.

Es un problema de interés en la industria y la acádemia, con aplicaciones variadas a distintas áreas del conocimiento.

- Análisis de sentimiento
- Determinación de autoría, e.g., género, edad, estilo, etc.
- Detección de spam
- Categorización de noticias
- Clasificación de idioma

# Procesamiento de Lenguaje Natural #

Un documento $d=s_1\cdots s_n$ es simplemente la concatenación de símbolos $s \in \Sigma$. Donde, $\Sigma$ es un _alfabeto_ de tamaño $\sigma = |\Sigma|$

Notese qué el número de textos posibles de tamaño $n$ es $\sigma^n$, por ejemplo, limitados a texto en inglés en Twitter se tienen
    $$ 26^{140} \simeq 1.248 \times 10^{198} $$

Sin emabargo, en lenguaje natural, este número no suele ser tan grande:
  - existen reglas sobre que símbolos se pueden unir
  - más aún, hay noción de _terminos_ o _palabras_, i.e., _morfología_
  - también, hay reglas sobre como las palabras se pueden combinar, i.e., _sintaxis y gramática_

Sin embargo, es un problema sumamente complicado, hay muchas reglas, variantes, excepciones, errores, etc.

Y por si fuera poco, aunque los conceptos existen en esencia, aparecen de manera diferente en todos los lenguajes

Además, esta el problema semántico:

- un término $s_i$ tiene significados diferentes (antónimos)
- lo contrario también existe, $s_i \not= s_j$ pero que son idénticos en significado (sinónimos)
- en ambos casos, el significado preciso depende del contexto
- también hay casos _aproximados_ de todo lo anterior
- ironias, sarcamos, etc.

... hay muchísimos problemas abiertos. **NLP** es complicado, de hecho es _AI-complete_

# Nuestro Enfoque #
Por su complejidad, trabajar en NLP tiene una gran cantidad de problemas abiertos, en particular nosotros nos enfocamos en la clasificación de texto escrito de manera informal (e.g., Twitter).

Para esto se utiliza un _pipeline_ estándar

No es la única opción, pero fijar el pipeline es útil como ya se expondrá.

El enfoque teórico suele ser muy complicado, y en realidad poco efectivo en la práctica, dadas las simplificaciones necesarias para hacerlo manejable

- Lógica
- Lingüistica
- Semántica

El enfoque práctico supone muchas cosas, en particular es un tanto casuístico:

- Se fija el lenguaje
- Se fija el problema
- Y la raíz de todos los males, muchas veces se supone que entre más técnicas sofísticadas se usen, mejores resultados se tendrán

En ambos enfoques se suele suponer que ausencia de errores de diferentes fuentes, sin embargo, es la regla cuando el texto que se analiza fue generado por usuarios de una red social, e.g. Twitter.

# ¿Qué es $\mu TC$? #
micro TC o $\mu TC$ es un clasificador de texto desarrollado en
el _Laboratorio de Análisis Computacional de Grandes Cúmulos de Información_
(o _Laboratorio de BigDATA_) de INFOTEC, sede Aguascalientes.

Esta disponible para ser clonado en [https://github.com/INGEOTEC/microTC](https://github.com/INGEOTEC/microTC). Esta escrito en Python 3.5 para sacar ventaja de unicode. También se puede instalar utilizando `pip` y `conda`.

En particular, nuestro enfoque se basa en _aprendizaje computacional_ y _optimización combinatoria_. Hemos probado que este esquema es muy competitivo en la práctica. Además, con la adecuada selección de las funciones podemos lograr que
$\mu TC$ se independiente del lenguaje y robusto a errores.

Esta compuesto por:
- una serie de funciones de transformación de texto
- una serie de tokenizadores
- filtros de palabras y
- algoritmos de pesado de términos

Todo esto orquestado mediante un algoritmo de optimización combinatoria

## Lista de parametros ##

### Normalizadores multilenguaje ###

|   nombre  | valores             |        descripción                   |
|-----------|---------------------|--------------------------------------|
|	del-punc | yes, no | Determina si las puntuaciones deben removerse |
|	del-d1   | yes, no | Determina si se deben borrar letras repetidas |
|	del-diac | yes, no | Determina si los simbolos que no ocupan espacios deben ser removidos |
|	lc       | yes, no | Determina si los símbolos deben ser normalizados en minúsculas |
|	emo      | remove, group, none | Controla como deben tratarse los emoticones |
|	num      | remove, group, none | `........................` números |
|	url      | remove, group, none | `........................` urls |
|	usr      | remove, group, none | `........................` usuarios |

configuraciones: 1296

### Normalizadores dependientes del lenguaje ###

|   nombre  | valores             |        descripción                   |
|-----------|---------------------|--------------------------------------|
|	stem   | yes, no | Determina si a las palabras se les aplica _stemming_. |
|	neg    | yes, no | Determina si los operadores de negación son manejados de manera especial |
|	sw | remove, group, none | Controla como los _stopwords_ son manejados |

configuraciones: 12

### Tokenizadores ###
Los tokenizadores son en realidad una lista de tokenizadores, y están definidos tokenizer un elemento en $\wp{(\text{n-words} \cup \text{q-grams} \cup \text{skip-grams})} \setminus \{\emptyset\}$

|   nombre  | valores             |        descripción                   |
|-----------|---------------------|--------------------------------------|
|	n-words    | $\{1,2,3\}$      | Longitud de n-gramas de palabras (n-words) |
|	q-grams  | $\{1,2,3,4,5,6,7\}$ | Longitud de q-gramas de caracteres) |
|	skip-grams  | $\{(2,1), (3, 1), (2, 2), (3, 2)\}$ | Lista de skip-grams|

configuraciones: 16383

### Parametros para pesado ###
|   nombre  | valores             |        descripción                   |
|-----------|---------------------|--------------------------------------|
|token_min_filter | $\{0.01, 0.03, 0.1, 0.30, -1, -5, -10\}$ | Filtro de frequencias bajas |
|token_max_filter | $\{0.9, 99, 1.0\}$ | Filtro de frequencias altas |
|	tfidf    | yes, no | Determina si se debe realizar un pesado TFIDF de terminos |

configuraciones = 42

```python
conf = 42 * 16383 * 12 * 1292
time = conf * 10 / 60 / 24 / 365.25
print(conf, time)
```

    10668085344 202830.73511293635

Con esto tenemos un total de $10,668,085,344$ configuraciones. Dependiendo del tamaño de cada colección, cada configuración se evalua en tiempo diferente.

Una tarea típica de análisis de sentimientos tiene un costo por configuración de cerca de 10 min. en una computadora de relativamente nueva, i.e., Intel(R) Xeon(R) CPU E5-2640 v3 @ 2.60GHz.

Esto da un total de $202,830.74$ años de tiempo de cómputo.

Un enfoque _naïve_ requiere una cantidad enorme de computadoras para parallelizar y distribuir este proceso, por esta razón, es mejor utilizar algoritmos eficientes para optimizar la búsqueda de la mejor configuración

```python
from sympy import symbols, sin, cos, exp, tan, sqrt
from sympy.plotting import plot, plot3d
from matplotlib import pyplot
x, y = symbols('x y')
plot(-x**2+200, axis=False, show=False).save("opt-1a.png")
plot(sin(x)*sin(1/(x*x)), axis=False, show=False).save('opt-1b.png')
plot3d((-x**2 - y**2, (x, -5, 5), (y, -5, 5)), axis=False, show=False).save('opt-2a.png')
plot3d(sin(x*y), axis=False, show=False).save('opt-2b.png')
```

# Optimización combinatoria #
Para tener algo práctico utilizamos una aproximación a encontrar la configuración óptima, en a lo más unas cuantas horas.

Para esto necesitamos un espacio de búsqueda (dominio) y una función de aptitud.

El espacio de búsqueda son todas las configuraciones.

La función de aptitud es una médida de _que tan atinado_ es nuestro clasificador. Deseamos seleccionar la configuración que maximice la función de aptitud.

Notesé que:

- El espacio de configuración no esta en $\mathbb{R}^n$
- En la configuración/parámetros son una mezcla de reales y categóricos
- No hay coordenadas como tal

Afortunadamente

- Se pueden definir vecindades entre configuraciones
- Se puede construir una gráfica
- Se puede samplear el espacio
- Se puede definir una función de aptitud de nodos

## Maximizando un objetivo (ejemplo) ##

|fácil|un poco más complicado|
|-|-|
|||
|||

# Resultados Experimentales #

# Gracias por su atención #

## ¿Comentarios y preguntas? ##

- Para más información sobre el _Posgrado en Ciencias en Ciencia de Datos_ aquí en Aguascalientes: <eric.tellez@infotec.mx>
- Esta presentación la puedes obtener en: <https://github.com/INGEOTEC/pres-MSA-SMM2016>
- $\mu TC$ lo puedes conseguir en: <https://github.com/INGEOTEC/microTC>
- Página de INFOTEC <https://www.infotec.mx/>

# Slides de soporte #

## Sobre el pesado ##
El pesado de tokens esta fijo a TFIDF. Su nombre viene de la formulación $tf \times idf$

$tf$ es _term frequency_; es una medida de importancia **local** del término $t$ en el documento $d$, de manera normalizada esta definida como:
    $$tf(t,d) = \frac{freq(t, d)}{\max_{w \in d}{freq(w, d)}}$$
entre más veces aparece en el documento $d$, $t$ es más importante

$idf$ quiere decir _inverse document frequency_; es una medida **global** a la colección $D$, esta definida como:
$$ idf(t,d) = log{\frac{|D|}{1+|{d \in D: t \in d}|}} $$
entre más veces aparece $t$ en la colección, el término es más común y menos discriminante; por lo tanto, menos importante

## Sobre el clasificador ##
El clasificador es un algoritmo de aprendizaje computacional que dado un objeto, decide finalmente la etiqueta o clase de ese objeto. Tiene dos etapas bien definidas

- **Entrenamiento.** Dado un conjunto de ejemplos en un espacio vectorial, con etiquetas, el algoritmo intenta _aprender_ las características que definen cada clase
- **Predicción.** La idea es que una vez entrenado, el algoritmo puede recibir objetos no vistos durante la etapa de entrenamiento y asignales la clase adecuada

En particular, esta fijo como un _Support Vector Machine_ (SVM) con kernel lineal

# ¿Por qué es robusto a errores?

Considere los siguientes textos $T=I\_like\_vanilla$, $T' = I\_lik3\_vanila$

Para fijar ideas pongamos que se usar el coeficiente de Jaccard como medida de similitud, i.e.

$$\frac{|\{{I, like, vanilla}\} \cap \{{I, lik3, vanila}\}|}{|\{{I, like, vanilla}\} \cup \{{I, lik3, vanila}\}|} = 0.2$$

$$Q^T_3 = \{ I\_l, \_li, lik, ike, ke\_, e\_v, \_va, van, ani, nil, ill, lla \}$$
$$Q^{T'}_3 = \{ I\_l, \_li, lik, ik3, k3\_, 3\_v, \_va, van, ani, nil, ila \}$$

Bajo la misma medida
$$\frac{|Q^T_3 \cap Q^{T'}_3|}{|Q^T_3 \cup Q^{T'}_3|} = 0.448.$$

Se puede ver que estos conjuntos son más similares que los tokenizados por palabra

La idea es que un algoritmo de aprendizaje tenga un poco más de soporte para determinar que $T$ es parecido a $T'$

```python
def emoformat(A, emo):
    s = "  ".join([a[0] for a in A if a[1] == emo])
    return s[:1000] + "..."

with open('microTC/microtc/resources/emoticons.json') as f:
    A = list(json.load(f).items())
    A.sort()
    S = dict(
        pos=emoformat(A, '_pos'),
        neg=emoformat(A, '_neg'),
        neu=emoformat(A, '_neu'),
        none=emoformat(A, '_none'),
    )

output = ["## Emoticones y emojis clasificados por sentimiento ##"]
for k, v in S.items():
    output.append("## Clase `{0}` ##".format(k.upper()))
    output.append(v)

Markdown("\n".join(output))
```

## Emoticones y emojis clasificados por sentimiento ##
## Clase `NONE` ##
'~'U  ^( \\\'-\\\' )^  π  ϟ  ಠ~ಠ  ‷̗ↂ凸ↂ‴  ‼  ⁂  ⁉  ⁎  ⁑  ℹ  ↶  ↷  ∨  ∩  ∪  ∴  ⊕  ⊗  ⊚  ⊛  ⊜  ⊥  ⌆  ⌘  ⌥  ⌫  ⎈  ⎋  ⎌  ⏎  ⏏  ╽  ╿  ◉  ●  ◐  ◑  ◒  ◓  ◔  ◕  ◖  ◗  ☄  ☇  ☈  ☉  ☊  ☌  ☍  ☎  ☏  ☛  ☝  ☞  ☤  ☧  ☨  ☩  ☪  ☫  ☬  ☭  ☸  ☿  ♊  ♋  ♌  ♍  ♎  ♏  ♐  ♑  ♒  ♓  ♤  ♨  ♭  ♮  ♯  ♿  ⚒  ⚓  ⚔  ⚕  ⚘  ⚚  ⚜  ⚪  ⚫  ✆  ✇  ✊  ✑  ✠  ✡  ✢  ✣  ✤  ✥  ✦  ✧  ❖  ❗  ❦  ❧  ➖  ➗  ➰  ➷  ➺  ➿  づ  ゞ  ガ  ギ  グ  ゲ  ゴ  ザ  ズ  ゼ  バ  ビ  ヷ  ヹ  ヺ  ㊊  ㊋  ㊌  ㊍  ㊎  ㊏  ㊐  유  ﹢  ﹣  ﹤  ﹥  🀄...
## Clase `POS` ##
(^L^)  (^‿^)  (°⌣°)  (•‿•)  (｡◕‿◕｡)  *-*  :)  :*  :-)  :-*  :-D  :3  :B  :D  :]  :p  ;)  ;-)  ;D  < (^^,) >  <3  <【☯】‿【☯】>  =)  =D  =^.^=  D:  Dx  XP  ^( \\\'‿\\\' )^  ^.^  ^^  ^_^  ^o^  ^‿^  n_n  q(❂‿❂)p  xD  {◕ ◡ ◕}  |◔◡◉|  ¢‿¢  Ü  ñ.ñ  ñ__ñ  ñ_ñ  ó‿ó  ت  ٩(-̮̮̃-̃)۶  ٩(-̮̮̃•̃)  ٩(^‿^)۶  ٩(͡๏̮͡๏)۶  ٩◔‿◔۶  ಠ◡ಠ  ༺‿༻  ღ  ᵔᴥᵔ  👨❤💋👨  👩❤💋👨  👩❤💋👩  †  ⇧  ⇪  √  ≧◡≦  ⊂◉‿◉つ  ⊙▃⊙  ⋆  ⍟  ┌(ಠ‿ಠ)┘  ╳  ●‿●  ◕‿◕٩(●̮̮̃•̃)۶  ◙‿◙  ☀  ☃  ★  ☆  ☋  ☑  ☕  ☘  ☚  ☜  ☥  ☦  ☮  ☯  ☺  ☻  ☼  ☽  ☾  ♉  ♔  ♕  ♖  ♚  ♛  ♡  ♣  ♥  ♥‿♥  ♧  ♩  ♪  ♫  ♬  ♻  ⚖  ⚛  ⛹  ⛹🏻  ⛹🏼  ⛹🏽  ⛹🏾  ⛹🏿  ✅  ✈  ✉  ✌  ✌🏻  ✌🏼  ✌🏽  ✌🏾  ✌🏿  ✓  ✔  ✙  ✚  ✛  ✜  ✝  ✞  ✟  ✨  ✩  ✪  ✫  ✬  ✭  ✮  ✯  ✰  ✱  ✲  ✳  ✴  ✵  ✶  ✷  ✸  ✹  ✺  ✻  ✼  ✽  ✾  ✿  ❀  ❀‿❀  ❁  ❂  ❃  ❇  ❈  ❉  ❊  ❋  ❕  ❣  ❤  ❤  ❤‿❤  ❥  ➕  ➳  ➵  ➸  ➹  ➻  ➼  ➽  ⭐  シ  ジ  ゾ  ッ  ツ  ヅ  ㋡  乂◜◬◝乂  웃  🌄  🌅  🌷  🌹  🌺  🌻  🌼  🍀  🍻  🍾  🎂  🎄  🎆  🎇  🎈  🎉  🎊  🎑  🎖  🎟  🎧  🎫  🎵  🎶  🎼  🏅  🏆  🏩  🏵  🐭  🐮  🐱  🐵  👋  👋🏻  👋🏼  👋🏽  👋🏾  👋🏿  👌  👌🏻  👌🏼  👌🏽  👌🏾  👌🏿  👍  👍🏻  👍🏼  👍🏽  👍🏾  👍🏿  👎  👎🏻  👎🏼  👎🏽  👎🏾  👎🏿  👏...
## Clase `NEU` ##
#⃣  $_$  &  (-\\\'\\\'-)  (•̪●)  *⃣  -.-  -_-  ._.  0⃣  1⃣  2⃣  3⃣  4⃣  5⃣  6⃣  7⃣  8-|  8⃣  9⃣  :-|  >:-o  @_@  U_U  n__n  u.u  u_u  v( ‘.’ )v  {•̃̾_•̃̾}  |˚–˚|  ©  ¬¬  ®  ఠ_ఠ  ರ_ರ  👨❤👨  👩❤👨  👩❤👩  ™  ↔  ↕  ↖  ↗  ↘  ↙  ↩  ↪  ⇎_⇎  ⌚  ⌨  ⏩  ⏪  ⏫  ⏬  ⏭  ⏮  ⏯  ⏰  ⏱  ⏲  ⏳  ⏸  ⏹  ⏺  Ⓜ  ▪  ▫  ▶  ◀  ◐.̃◐  ◑.◑  ◔_◔  ◻  ◼  ◽  ◾  ☁  ☝🏻  ☝🏼  ☝🏽  ☝🏾  ☝🏿  ♀  ♁  ♂  ♈  ♗  ♘  ♙  ♜  ♝  ♞  ♟  ♠  ♢  ♦  ⚗  ⚙  ⚰  ⚱  ⚽  ⚾  ⛄  ⛅  ⛈  ⛎  ⛏  ⛑  ⛓  ⛩  ⛪  ⛰  ⛱  ⛲  ⛳  ⛴  ⛵  ⛷  ⛸  ⛺  ⛽  ✋🏻  ✋🏼  ✋🏽  ✋🏾  ✋🏿  ✍  ✍🏻  ✍🏼  ✍🏽  ✍🏾  ✍🏿  ✎  ✏  ✒  ❄  ❅  ❆  ❓  ❔  ➡  ⤴  ⤵  ⬅  ⬆  ⬇  ⬛  ⬜  ⭕  【•】_【•】  〰  〽  ㊗  ㊙  🃏  🅰  🅱  🅾  🅿  🆎  🆑  🆒  🆓  🆔  🆕  🆖  🆗  🆘  🆙  🆚  🇦🇨  🇦🇩  🇦🇪  🇦🇫  🇦🇬  🇦🇮  🇦🇱  🇦🇲  🇦🇴  🇦🇶  🇦🇷  🇦🇸  🇦🇹  🇦🇺  🇦🇼  🇦🇽  🇦🇿  🇧🇦  🇧🇧  🇧🇩  🇧🇪  🇧🇫  🇧🇬  🇧🇭  🇧🇮  🇧🇯  🇧🇱  🇧🇲  🇧🇳  🇧🇴  🇧🇶  🇧🇷  🇧🇸  🇧🇹  🇧🇻  🇧🇼  🇧🇾  🇧🇿  🇨🇦  🇨🇨  🇨🇩  🇨🇫  🇨🇬  🇨🇭  🇨🇮  🇨🇰  🇨🇱  🇨🇲  🇨🇳  🇨🇴  🇨🇵  🇨🇷  🇨🇺  🇨🇻  🇨🇼  🇨🇽  🇨🇾  🇨🇿  🇩🇪  🇩🇬  🇩🇯  🇩🇰  🇩🇲  🇩🇴  🇩🇿  🇪🇦  🇪🇨  🇪🇪  🇪🇬  🇪🇭  🇪🇷  🇪🇸  🇪🇹  🇪🇺  🇫🇮  🇫🇯  🇫🇰  🇫🇲  🇫🇴  🇫🇷  🇬🇦  🇬🇧  🇬🇩  🇬🇪  🇬🇫  🇬🇬  🇬🇭  🇬🇮  🇬🇱  🇬🇲  🇬🇳  🇬🇵 ...
## Clase `NEG` ##
'n'  (>.<)  (O.O)  (⊙̃.o ⊙.◎)  )-:  ):  ):-/  .l.  /:  :"(  :'(  :(  :-(  :-/  :-\  :-o  :-x  :/  :S  :[  :\  :c  :o  ='(  =(  =S  >.<  >:-(  >:o  ><  >_<  O.o  O.ó  TT  XC  X_X  \\\\m/(>.<)\\\\m/  o.Ó  õ.O  x_x  ¬ ¬  ¬ ¬*  ¬¬*  ».«  ò_ó  ॓_॔  ๏_๏  ⊝  ⌛  ⌤  ☂  ☒  ☔  ☟  ☠  ☢  ☣  ☹  ⚠  ⚡  ⛔  ✂  ✊🏻  ✊🏼  ✊🏽  ✊🏾  ✊🏿  ✋  ✕  ✖  ✗  ✘  ❌  ❎  〴⋋_⋌〵  卍  卐  🌁  🎗  👹  👺  👻  👽  👾  👿  💀  💔  💢  💣  💦  💨  💩  💫  📉  🖕  🖕🏻  🖕🏼  🖕🏽  🖕🏾  🖕🏿  🗯  😓  😔  😖  😞  😟  😠  😡  😢  😣  😥  😦  😧  😨  😩  😪  😫  😬  😭  😮  😯  😰  😱  😲  😳  😵  😷  😾  😿  🙀  🙁  🙃  🙅  🙅🏻  🙅🏼  🙅🏽  🙅🏾  🙅🏿  🙈  🙉  🙊  🙎  🙎🏻  🙎🏼  🙎🏽  🙎🏾  🙎🏿  🚫  🚳  🤒  🤕  🤘  🤘🏻  🤘🏼  🤘🏽  🤘🏾  🤘🏿...

```python
from itertools import combinations
text = "qué buena esta la plática"
tokenizers = [1, 2, 3, -1, -2, (2,1), (2,2)]

num = 1
output = ["""

## Tokenizadores Ejemplos ##
La codificación es:

- entero positivo: q-grama de caracter
- entero engativo: w-word (n-grama de palabra)
- tupla: skip-gram (tamaño, salto)

"""]
for ltokens in range(len(tokenizers)):
    output.append('## Combinaciones de tamaño {0} ##'.format(ltokens+1))
    output.append('|id|combinación|tokens|')
    output.append('|--|-----------|------|')
    for comb in combinations(tokenizers, ltokens+1):
        model = TextModel([], token_list=comb)
        output.append("|{0}|{1}|{2}|".format(num, comb, ", ".join(model.tokenize(text))))
        num += 1

Markdown("\n".join(output))
```

## Tokenizadores Ejemplos ##
La codificación es:

- entero positivo: q-grama de caracter
- entero engativo: w-word (n-grama de palabra)
- tupla: skip-gram (tamaño, salto)

## Combinaciones de tamaño 1 ##
|id|combinación|tokens|
|--|-----------|------|
|1|(1,)|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~|
|2|(2,)|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~|
|3|(3,)|~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~|
|4|(-1,)|que, buena, esta, la, platica|
|5|(-2,)|que~buena, buena~esta, esta~la, la~platica|
|6|((2, 1),)|que~esta, buena~la, esta~platica|
|7|((2, 2),)|que~la, buena~platica|
## Combinaciones de tamaño 2 ##
|id|combinación|tokens|
|--|-----------|------|
|8|(1, 2)|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~|
|9|(1, 3)|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~|
|10|(1, -1)|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, que, buena, esta, la, platica|
|11|(1, -2)|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, que~buena, buena~esta, esta~la, la~platica|
|12|(1, (2, 1))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, que~esta, buena~la, esta~platica|
|13|(1, (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, que~la, buena~platica|
|14|(2, 3)|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~|
|15|(2, -1)|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que, buena, esta, la, platica|
|16|(2, -2)|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que~buena, buena~esta, esta~la, la~platica|
|17|(2, (2, 1))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que~esta, buena~la, esta~platica|
|18|(2, (2, 2))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que~la, buena~platica|
|19|(3, -1)|~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica|
|20|(3, -2)|~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~buena, buena~esta, esta~la, la~platica|
|21|(3, (2, 1))|~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~esta, buena~la, esta~platica|
|22|(3, (2, 2))|~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~la, buena~platica|
|23|(-1, -2)|que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica|
|24|(-1, (2, 1))|que, buena, esta, la, platica, que~esta, buena~la, esta~platica|
|25|(-1, (2, 2))|que, buena, esta, la, platica, que~la, buena~platica|
|26|(-2, (2, 1))|que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica|
|27|(-2, (2, 2))|que~buena, buena~esta, esta~la, la~platica, que~la, buena~platica|
|28|((2, 1), (2, 2))|que~esta, buena~la, esta~platica, que~la, buena~platica|
## Combinaciones de tamaño 3 ##
|id|combinación|tokens|
|--|-----------|------|
|29|(1, 2, 3)|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~|
|30|(1, 2, -1)|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que, buena, esta, la, platica|
|31|(1, 2, -2)|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que~buena, buena~esta, esta~la, la~platica|
|32|(1, 2, (2, 1))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que~esta, buena~la, esta~platica|
|33|(1, 2, (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que~la, buena~platica|
|34|(1, 3, -1)|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica|
|35|(1, 3, -2)|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~buena, buena~esta, esta~la, la~platica|
|36|(1, 3, (2, 1))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~esta, buena~la, esta~platica|
|37|(1, 3, (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~la, buena~platica|
|38|(1, -1, -2)|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica|
|39|(1, -1, (2, 1))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, que, buena, esta, la, platica, que~esta, buena~la, esta~platica|
|40|(1, -1, (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, que, buena, esta, la, platica, que~la, buena~platica|
|41|(1, -2, (2, 1))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica|
|42|(1, -2, (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, que~buena, buena~esta, esta~la, la~platica, que~la, buena~platica|
|43|(1, (2, 1), (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, que~esta, buena~la, esta~platica, que~la, buena~platica|
|44|(2, 3, -1)|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica|
|45|(2, 3, -2)|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~buena, buena~esta, esta~la, la~platica|
|46|(2, 3, (2, 1))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~esta, buena~la, esta~platica|
|47|(2, 3, (2, 2))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~la, buena~platica|
|48|(2, -1, -2)|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica|
|49|(2, -1, (2, 1))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que, buena, esta, la, platica, que~esta, buena~la, esta~platica|
|50|(2, -1, (2, 2))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que, buena, esta, la, platica, que~la, buena~platica|
|51|(2, -2, (2, 1))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica|
|52|(2, -2, (2, 2))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que~buena, buena~esta, esta~la, la~platica, que~la, buena~platica|
|53|(2, (2, 1), (2, 2))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que~esta, buena~la, esta~platica, que~la, buena~platica|
|54|(3, -1, -2)|~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica|
|55|(3, -1, (2, 1))|~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~esta, buena~la, esta~platica|
|56|(3, -1, (2, 2))|~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~la, buena~platica|
|57|(3, -2, (2, 1))|~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica|
|58|(3, -2, (2, 2))|~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~buena, buena~esta, esta~la, la~platica, que~la, buena~platica|
|59|(3, (2, 1), (2, 2))|~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~esta, buena~la, esta~platica, que~la, buena~platica|
|60|(-1, -2, (2, 1))|que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica|
|61|(-1, -2, (2, 2))|que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~la, buena~platica|
|62|(-1, (2, 1), (2, 2))|que, buena, esta, la, platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
|63|(-2, (2, 1), (2, 2))|que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
## Combinaciones de tamaño 4 ##
|id|combinación|tokens|
|--|-----------|------|
|64|(1, 2, 3, -1)|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica|
|65|(1, 2, 3, -2)|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~buena, buena~esta, esta~la, la~platica|
|66|(1, 2, 3, (2, 1))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~esta, buena~la, esta~platica|
|67|(1, 2, 3, (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~la, buena~platica|
|68|(1, 2, -1, -2)|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica|
|69|(1, 2, -1, (2, 1))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que, buena, esta, la, platica, que~esta, buena~la, esta~platica|
|70|(1, 2, -1, (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que, buena, esta, la, platica, que~la, buena~platica|
|71|(1, 2, -2, (2, 1))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica|
|72|(1, 2, -2, (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que~buena, buena~esta, esta~la, la~platica, que~la, buena~platica|
|73|(1, 2, (2, 1), (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que~esta, buena~la, esta~platica, que~la, buena~platica|
|74|(1, 3, -1, -2)|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica|
|75|(1, 3, -1, (2, 1))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~esta, buena~la, esta~platica|
|76|(1, 3, -1, (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~la, buena~platica|
|77|(1, 3, -2, (2, 1))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica|
|78|(1, 3, -2, (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~buena, buena~esta, esta~la, la~platica, que~la, buena~platica|
|79|(1, 3, (2, 1), (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~esta, buena~la, esta~platica, que~la, buena~platica|
|80|(1, -1, -2, (2, 1))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica|
|81|(1, -1, -2, (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~la, buena~platica|
|82|(1, -1, (2, 1), (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, que, buena, esta, la, platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
|83|(1, -2, (2, 1), (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
|84|(2, 3, -1, -2)|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica|
|85|(2, 3, -1, (2, 1))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~esta, buena~la, esta~platica|
|86|(2, 3, -1, (2, 2))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~la, buena~platica|
|87|(2, 3, -2, (2, 1))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica|
|88|(2, 3, -2, (2, 2))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~buena, buena~esta, esta~la, la~platica, que~la, buena~platica|
|89|(2, 3, (2, 1), (2, 2))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~esta, buena~la, esta~platica, que~la, buena~platica|
|90|(2, -1, -2, (2, 1))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica|
|91|(2, -1, -2, (2, 2))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~la, buena~platica|
|92|(2, -1, (2, 1), (2, 2))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que, buena, esta, la, platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
|93|(2, -2, (2, 1), (2, 2))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
|94|(3, -1, -2, (2, 1))|~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica|
|95|(3, -1, -2, (2, 2))|~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~la, buena~platica|
|96|(3, -1, (2, 1), (2, 2))|~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
|97|(3, -2, (2, 1), (2, 2))|~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
|98|(-1, -2, (2, 1), (2, 2))|que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
## Combinaciones de tamaño 5 ##
|id|combinación|tokens|
|--|-----------|------|
|99|(1, 2, 3, -1, -2)|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica|
|100|(1, 2, 3, -1, (2, 1))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~esta, buena~la, esta~platica|
|101|(1, 2, 3, -1, (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~la, buena~platica|
|102|(1, 2, 3, -2, (2, 1))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica|
|103|(1, 2, 3, -2, (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~buena, buena~esta, esta~la, la~platica, que~la, buena~platica|
|104|(1, 2, 3, (2, 1), (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~esta, buena~la, esta~platica, que~la, buena~platica|
|105|(1, 2, -1, -2, (2, 1))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica|
|106|(1, 2, -1, -2, (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~la, buena~platica|
|107|(1, 2, -1, (2, 1), (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que, buena, esta, la, platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
|108|(1, 2, -2, (2, 1), (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
|109|(1, 3, -1, -2, (2, 1))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica|
|110|(1, 3, -1, -2, (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~la, buena~platica|
|111|(1, 3, -1, (2, 1), (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
|112|(1, 3, -2, (2, 1), (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
|113|(1, -1, -2, (2, 1), (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
|114|(2, 3, -1, -2, (2, 1))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica|
|115|(2, 3, -1, -2, (2, 2))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~la, buena~platica|
|116|(2, 3, -1, (2, 1), (2, 2))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
|117|(2, 3, -2, (2, 1), (2, 2))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
|118|(2, -1, -2, (2, 1), (2, 2))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
|119|(3, -1, -2, (2, 1), (2, 2))|~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
## Combinaciones de tamaño 6 ##
|id|combinación|tokens|
|--|-----------|------|
|120|(1, 2, 3, -1, -2, (2, 1))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica|
|121|(1, 2, 3, -1, -2, (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~la, buena~platica|
|122|(1, 2, 3, -1, (2, 1), (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
|123|(1, 2, 3, -2, (2, 1), (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
|124|(1, 2, -1, -2, (2, 1), (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
|125|(1, 3, -1, -2, (2, 1), (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
|126|(2, 3, -1, -2, (2, 1), (2, 2))|~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica, que~la, buena~platica|
## Combinaciones de tamaño 7 ##
|id|combinación|tokens|
|--|-----------|------|
|127|(1, 2, 3, -1, -2, (2, 1), (2, 2))|~, q, u, e, ~, b, u, e, n, a, ~, e, s, t, a, ~, l, a, ~, p, l, a, t, i, c, a, ~, ~q, qu, ue, e~, ~b, bu, ue, en, na, a~, ~e, es, st, ta, a~, ~l, la, a~, ~p, pl, la, at, ti, ic, ca, a~, ~qu, que, ue~, e~b, ~bu, bue, uen, ena, na~, a~e, ~es, est, sta, ta~, a~l, ~la, la~, a~p, ~pl, pla, lat, ati, tic, ica, ca~, que, buena, esta, la, platica, que~buena, buena~esta, esta~la, la~platica, que~esta, buena~la, esta~platica, que~la, buena~platica|


```python
#remove cell visibility
from IPython.display import HTML
tag = HTML('''
Toggle cell visibility <a href="javascript:code_toggle()">here</a>.''')
display(tag)
```

Toggle cell visibility here.

```python
%matplotlib inline
import control
import numpy
import sympy as sym
from IPython.display import display, Markdown
import ipywidgets as widgets
import matplotlib.pyplot as plt

#print a matrix latex-like
def bmatrix(a):
     """Returns a LaTeX bmatrix - by Damir Arbula (ICCT project)

     :a: numpy array
     :returns: LaTeX bmatrix as a string
     """
     if len(a.shape) > 2:
         raise ValueError('bmatrix can at most display two dimensions')
     lines = str(a).replace('[', '').replace(']', '').splitlines()
     rv = [r'\begin{bmatrix}']
     rv += ['  ' + ' & '.join(l.split()) + r'\\' for l in lines]
     rv +=  [r'\end{bmatrix}']
     return '\n'.join(rv)

# Display formatted matrix:
def vmatrix(a):
    if len(a.shape) > 2:
         raise ValueError('bmatrix can at most display two dimensions')
    lines = str(a).replace('[', '').replace(']', '').splitlines()
    rv = [r'\begin{vmatrix}']
    rv += ['  ' + ' & '.join(l.split()) + r'\\' for l in lines]
    rv +=  [r'\end{vmatrix}']
    return '\n'.join(rv)

#matrixWidget is a matrix looking widget built with a VBox of HBox(es) that returns a numPy array as value !
class matrixWidget(widgets.VBox):
    def updateM(self,change):
        for irow in range(0,self.n):
            for icol in range(0,self.m):
                self.M_[irow,icol] = self.children[irow].children[icol].value
                #print(self.M_[irow,icol])
        self.value = self.M_

    def dummychangecallback(self,change):
        pass

    def __init__(self,n,m):
        self.n = n
        self.m = m
        self.M_ = numpy.matrix(numpy.zeros((self.n,self.m)))
        self.value = self.M_
        widgets.VBox.__init__(self,
                             children = [
                                 widgets.HBox(children =
                                              [widgets.FloatText(value=0.0, layout=widgets.Layout(width='90px')) for i in range(m)]
                                             )
                                 for j in range(n)
                             ])

        #fill in widgets and tell interact to call updateM each time a children changes value
        for irow in range(0,self.n):
            for icol in range(0,self.m):
                self.children[irow].children[icol].value = self.M_[irow,icol]
                self.children[irow].children[icol].observe(self.updateM, names='value')
        #value = Unicode('example@example.com', help="The email value.").tag(sync=True)
        self.observe(self.updateM, names='value', type= 'All')

    def setM(self, newM):
        #disable callbacks, change values, and reenable
        self.unobserve(self.updateM, names='value', type= 'All')
        for irow in range(0,self.n):
            for icol in range(0,self.m):
                self.children[irow].children[icol].unobserve(self.updateM, names='value')
        self.M_ = newM
        self.value = self.M_
        for irow in range(0,self.n):
            for icol in range(0,self.m):
                self.children[irow].children[icol].value = self.M_[irow,icol]
        for irow in range(0,self.n):
            for icol in range(0,self.m):
                self.children[irow].children[icol].observe(self.updateM, names='value')
        self.observe(self.updateM, names='value', type= 'All')

                #self.children[irow].children[icol].observe(self.updateM, names='value')

#overlaod class for state space systems that DO NOT remove "useless" states (what "professor" of automatic control would do this?)
class sss(control.StateSpace):
    def __init__(self,*args):
        #call base class init constructor
        control.StateSpace.__init__(self,*args)
    #disable function below in base class
    def _remove_useless_states(self):
        pass
```

## Krmiljenje longitudinalne hitrosti kvadrotorja

Longitudinalno hitrost kvadrotorja (ali kvadrokopterja) lahko krmilimo z nagibom kvadrotorja prek njegovega kota okoli prečne osi. Kot $\theta$ krmilimo prek navora $T$ z uporabo propelerjev. Vztrajnostni moment vozila je $J= 1.3e-2$. Ko je vozilo nagnjeno za kot $\theta$, producirajo propelerji vlečno silo, ki je približno enaka $F_v = F\theta = mg\theta$, zračni upor je $F_c=-cv=-0.9v$, $m=2000$ g je masa vozila, $g = 9.81$ m/s2 pa težnostni pospešek. Maksimalen navor znaša $5000$ mNm. Kot vozila okoli prečne osi $\theta$ mora biti omejen na $\pm30$ stopinj skozi celotno delovanje kvadrotorja, maksimalna hitrost pa na 2 m/s. Kot vozila okoli prečne osi je ocenjen na podlagi ustreznega senzorja in hitrosti, merjene z uporabo GPS.

Postopek načrtovanja krmiljenja sestoji iz dveh korakov:
1. Zapis enačb sistema v obliki prostora stanj za rotacijsko dinamiko (navor $T$ in kot vozila okoli prečne osi $\theta$) ter longitudinalno dinamiko (od kota vozila okoli prečne osi do naprej usmerjene hitrosti $v$).
2. Načrtovanje regulatorja za $v$ ob upoštevanju naslednjih zahtev:
    - Čas ustalitve krajši od 2.5 s (dosežena vrednost izhoda naj se razlikuje od tiste v stacionarnem stanju za 5%).
    - Brez prenihaja.
    - Brez odstopka v stacionarnem stanju v odzivu na vhodno koračno funkcijo hitrosti.

### Enačbe sistema

Enačbi sistema sta:

\begin{cases}
    m\dot{v} = F_v + F_c = mg\theta -cv \\
    J\ddot{\theta} = T.
\end{cases}

Ob upoštevanju vektorja stanj $x = \begin{bmatrix} x_1 & x_2 & x_3 \end{bmatrix}^T = \begin{bmatrix} v & \theta & \dot{\theta} \end{bmatrix}^T$ ter vhoda $u=T$, lahko zapišemo enačbe sistema v obliki prostora stanj:

\begin{cases}
\dot{x} = \begin{bmatrix} -c/m & g & 0 \\ 0 & 0 & 1 \\ 0 & 0 & 0 \end{bmatrix}x + \begin{bmatrix} 0 \\ 0 \\ 1/J \end{bmatrix}u \\
y = \begin{bmatrix} y_1 \\ y_2 \end{bmatrix} = \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \end{bmatrix}x
\end{cases}

Dinamika od $u$ do $\theta$ je dvojni integrator, medtem ko je tista od $\theta$ do $v$ dinamika prvega reda s polom v $-c/m$. Sistem ima dva izhoda: hitrost in kot nagiba vozila.

Vodljivostna matrika $\mathcal{C}$ je

```python
A = numpy.matrix('-0.45 9.81 0; 0 0 1; 0 0 0')
B = numpy.matrix([[0],[0],[1/1.3E-02]])
C = numpy.matrix('1 0 0; 0 1 0')
D = numpy.matrix('0; 0')

CM = control.ctrb(A,B)
display(Markdown(bmatrix(CM)))
# print(numpy.linalg.matrix_rank(CM))
```

\begin{bmatrix}
  0. & 0. & 754.61538462\\
  0. & 76.92307692 & 0.\\
  76.92307692 & 0. & 0.\\
\end{bmatrix}

in ima rang enak 3, kar pomeni, da je sistem vodljiv.
Spoznavnostna matrija $\mathcal{O}$ je

```python
OM = control.obsv(A,C)
display(Markdown(bmatrix(OM)))
# print(numpy.linalg.matrix_rank(OM))
```

\begin{bmatrix}
  1. & 0. & 0.\\
  0. & 1. & 0.\\
  -0.45 & 9.81 & 0.\\
  0. & 0. & 1.\\
  0.2025 & -4.4145 & 9.81\\
  0. & 0. & 0.\\
\end{bmatrix}

in ima rang enak 3, kar pomeni da je sistem spoznaven.

### Načrovanje regulatorja
#### Načrtovanje spoznavalnika
Ker neposredno merimo $x_1$ in $x_2$, nas zanima le ocena stanja $x_3$. Če pogledamo podsistem $(x_2, \, x_3)$, opazimo, da je spoznaven, tako da je možno načrtovati spoznavalnik zgolj za ta podsistem. Zgradba spoznavalnika je tako:

$$
\begin{bmatrix} \dot{\hat{x}_2} \\ \dot{\hat{x}_3} \end{bmatrix} = \begin{bmatrix} 0 & 1 \\ 0 & 0 \end{bmatrix}\begin{bmatrix} \hat{x}_2 \\ \hat{x}_3 \end{bmatrix} + \begin{bmatrix} 0 \\ 1/J \end{bmatrix}u + \begin{bmatrix} l_1 \\ l_2 \end{bmatrix}\left( y - C\begin{bmatrix} \hat{x}_2 \\ \hat{x}_3 \end{bmatrix} \right) = \begin{bmatrix} -l_1 & 1 \\ -l_2 & 0 \end{bmatrix}\begin{bmatrix} \hat{x}_2 \\ \hat{x}_3 \end{bmatrix} + \begin{bmatrix} 0 \\ 1/J \end{bmatrix}u + \begin{bmatrix} l_1 \\ l_2 \end{bmatrix}y
$$

z uporabo Laplaceove transformacije in rešitvijo za $\hat{x}_3(s)$, dobimo

$$
\hat{x}_3(s) = \frac{l_2s}{s^2+l_1s+l_2}y_2(s) + \frac{s+l_1}{s^2+l_1s+l_2}\frac{u(s)}{J}.
$$

Dobili smo enostaven linearen spoznavalnik za $x_3$, ki je asimptotično stabilen za vsak $l_1>0$ in $l_2>0$. Zanimivo je, da če velja $l_2\rightarrow \infty$, se prenosna funkcija spoznavalnika poenostavi v $\hat{x}_3(s) = s y_2(s)$, rezultat pa je enak $\hat{x}_3 = \dot{\theta}$; slednjega dobimo z odvodom izmerjene spremenljivke $y_2 = \theta$.

Z izbiro $l_1 = 20$ in $l_2 = 100$ sta obe lastni vrednosti spoznavalnika enaki $-10$.

#### Načrtovanje krmilnika
Za izpolnitev zahteve glede časa ustalitve, mora biti frekvenca polov večja kot $3/T_S$ (v primeru realnih polov) oz. $3/\zeta Ts$ (v primeru kompleksnih polov), pri čemer sta $T_s$ čas ustalitve, $\zeta$ pa razmernik dušenja. Dobre lokacije polov (v smislu odziva in vhodne energije) za sistem z dvojnim integratorjem, so v območju $\pm 45°$ glede na negativno realno os. Z upoštevanjem teh dejstvem, izberemo dominantna pola v $-2.8\pm1.0i$, medtem ko je tretji pol razporedimo v $-15$.
Za izpolnitev zahteve glede odstopka v stacionarnem stanju, moramo vhod skalirati z ojačanjem, ki je enak inverzu ojačanja zaprtozančnega sistema v stacionarnem stanju, da dosežemo, da je vrednost celotnega zaprtozančnega ojačanja enako $1.0$.

### Kako upravljati s tem interaktivnim primerom?
- Preveri izpolnjevanje danih zahtev v primeru začetne napake ocene stanja $x_3$ (tako za pozitivne kot negativne vrednosti napake).
- Opazuj spremembe odziva sistema in, ob upoštevanju fizičnega sistema, poizkusi razumeti, zakaj se odziv spremeni na tak način, kot se.

```python
# Preparatory cell

X0 = 0.0
K = numpy.matrix([8/15,-4.4,-4])
L = numpy.matrix([[66],[107/3]])

Aw = matrixWidget(3,3)
Aw.setM(A)
Bw = matrixWidget(3,1)
Bw.setM(B)
Cw = matrixWidget(1,3)
Cw.setM(C)
X0w = widgets.FloatText(
    value=X0,
    description='',
    disabled=False
)
Kw = matrixWidget(1,3)
Kw.setM(K)
Lw = matrixWidget(2,1)
Lw.setM(L)

eig1c = matrixWidget(1,1)
eig2c = matrixWidget(2,1)
eig3c = matrixWidget(1,1)
eig1c.setM(numpy.matrix([-15.]))
eig2c.setM(numpy.matrix([[-2.8],[-1.0]]))
eig3c.setM(numpy.matrix([-15.]))

eig2o = matrixWidget(2,1)
eig3o = matrixWidget(1,1)
eig2o.setM(numpy.matrix([[-10.],[0.]]))
eig3o.setM(numpy.matrix([-10.]))
```

```python
# Misc

#create dummy widget
DW = widgets.FloatText(layout=widgets.Layout(width='0px', height='0px'))

#create button widget
START = widgets.Button(
    description='Test',
    disabled=False,
    button_style='', # 'success', 'info', 'warning', 'danger' or ''
    tooltip='Test',
    icon='check'
)

def on_start_button_clicked(b):
    #This is a workaround to have intreactive_output call the callback:
    #   force the value of the dummy widget to change
    if DW.value> 0 :
        DW.value = -1
    else:
        DW.value = 1
    pass
START.on_click(on_start_button_clicked)

# Define type of method
selm = widgets.Dropdown(
    options= ['Nastavi K in L', 'Nastavi lastne vrednosti'],
    value= 'Nastavi lastne vrednosti',
    description='',
    disabled=False
)

# Define the number of complex eigenvalues
sele = widgets.Dropdown(
    options= ['brez kompleksnih lastnih vrednosti', 'dve kompleksni lastni vrednosti'],
    value= 'dve kompleksni lastni vrednosti',
    description='Kompleksne lastne vrednosti:',
    style = {'description_width': 'initial'},
    disabled=False
)

#define type of ipout
selu = widgets.Dropdown(
    options=['impulzna funkcija', 'koračna funkcija', 'sinusoidna funkcija', 'kvadratni val'],
    value='koračna funkcija',
    description='Vhod:',
    style = {'description_width': 'initial'},
    disabled=False
)
# Define the values of the input
u = widgets.FloatSlider(
    value=2,
    min=0,
    max=4,
    step=0.1,
    description='Vhod:',
    disabled=False,
    continuous_update=False,
    orientation='horizontal',
    readout=True,
    readout_format='.1f',
)
period = widgets.FloatSlider(
    value=0.5,
    min=0.001,
    max=10,
    step=0.001,
    description='Perioda: ',
    disabled=False,
    continuous_update=False,
    orientation='horizontal',
    readout=True,
    readout_format='.2f',
)

gain_w2 = widgets.FloatText(
    value=1.,
    description='',
    disabled=True
)

simTime = widgets.FloatText(
    value=3,
    description='',
    disabled=False
)
```

```python
# Support functions

def eigen_choice(sele):
    if sele == 'brez kompleksnih lastnih vrednosti':
        eig1c.children[0].children[0].disabled = False
        eig2c.children[1].children[0].disabled = True
        eig2o.children[1].children[0].disabled = True
        eig = 0
    if sele == 'dve kompleksni lastni vrednosti':
        eig1c.children[0].children[0].disabled = True
        eig2c.children[1].children[0].disabled = False
        eig2o.children[1].children[0].disabled = False
        eig = 2
    return eig

def method_choice(selm):
    if selm == 'Nastavi K in L':
        method = 1
        sele.disabled = True
    if selm == 'Nastavi lastne vrednosti':
        method = 2
        sele.disabled = False
    return method
```

```python
# Reduced system
Ar = numpy.matrix('0 1; 0 0')
Br = numpy.matrix([[0],[1/1.3E-02]])
Cr = numpy.matrix('1 0')
Dr = numpy.matrix('0')

def main_callback2(Aw, Bw, X0w, K, L, eig1c, eig2c, eig3c, eig2o, eig3o, u, period, selm, sele, selu, simTime, DW):
    eige = eigen_choice(sele)
    method = method_choice(selm)

    if method == 1:
        solc = numpy.linalg.eig(A-B*K)
        solo = numpy.linalg.eig(Ar-L*Cr)
    if method == 2:
        if eige == 0:
            K = control.acker(A, B, [eig1c[0,0], eig2c[0,0], eig3c[0,0]])
            Kw.setM(K)

            L = control.acker(Ar.T, Cr.T, [eig2o[0,0], eig3o[0,0]]).T
            Lw.setM(L)
        if eige == 2:
            K = control.acker(A, B, [eig3c[0,0],
                                     numpy.complex(eig2c[0,0],eig2c[1,0]),
                                     numpy.complex(eig2c[0,0],-eig2c[1,0])])
            Kw.setM(K)

            L = control.acker(Ar.T, Cr.T, [numpy.complex(eig2o[0,0],eig2o[1,0]),
                                           numpy.complex(eig2o[0,0],-eig2o[1,0])]).T
            Lw.setM(L)

    sys = control.ss(A,B,numpy.vstack((C,numpy.zeros((B.shape[1],C.shape[1])))),numpy.vstack((D,numpy.eye(B.shape[1]))))
    sysC = control.ss(numpy.zeros((1,1)),
                      numpy.zeros((1,numpy.shape(A)[0])),
                      numpy.zeros((numpy.shape(B)[1],1)),
                      -K)

    sysE = control.ss(Ar-L*Cr,
                      numpy.hstack((L,Br-L*Dr)),
                      numpy.matrix('0 1'),
                      numpy.zeros((1,2)))

    sys_append = control.append(sys, sysE, sysC, control.ss(A,B,numpy.eye(A.shape[0]),numpy.zeros((A.shape[0],B.shape[1]))))
    Q = []
    # y in ingresso a sysE
    for i in range(1):
        Q.append([B.shape[1]+i+1, i+2])
    # u in ingresso a sysE
    for i in range(B.shape[1]):
        Q.append([B.shape[1]+1+i+1, C.shape[0]+i+1])
    # u in ingresso a sys
    for i in range(B.shape[1]):
        Q.append([i+1, C.shape[0]+B.shape[1]+1+i+1])
    # u in ingresso al sistema che ha come uscite gli stati reali
    for i in range(B.shape[1]):
        Q.append([2*B.shape[1]+1+A.shape[0]+i+1, C.shape[0]+i+1])
    # xe in ingresso a sysC
    Q.append([2*B.shape[1]+1+1, 1])
    Q.append([2*B.shape[1]+1+1+1, 1+1])
    Q.append([2*B.shape[1]+1+2+1, C.shape[0]+B.shape[1]+1])

    inputv = [i+1 for i in range(B.shape[1])]
    outputv = [i+1 for i in range(numpy.shape(sys_append.C)[0])]
    sys_CL = control.connect(sys_append,
                             Q,
                             inputv,
                             outputv)

    t = numpy.linspace(0, 100000, 2)
    t, yout = control.step_response(sys_CL[0,0],T=t)
    dcgain = yout[-1]
    gain_w2.value = dcgain
    if dcgain != 0:
        u1 = u/gain_w2.value
    else:
        print('Nastavljena vrednost inverza ojačanja je enaka 0 in je bila spremenjena na vrednost 1')
        u1 = u/1
    print('Statično ojačanje zaprtozančnega sistema (od vhoda do izhoda) je: %.5f' %dcgain)

    X0w1 = numpy.zeros((2*A.shape[0]+2,1))
    X0w1[A.shape[0]+1,0] = X0w
    if simTime != 0:
        T = numpy.linspace(0, simTime, 10000)
    else:
        T = numpy.linspace(0, 1, 10000)

    if selu == 'impulzna funkcija': #selu
        U = [0 for t in range(0,len(T))]
        U[0] = u
        U1 = [0 for t in range(0,len(T))]
        U1[0] = u1
        T, yout, xout = control.forced_response(sys_CL,T,U1,X0w1)
    if selu == 'koračna funkcija':
        U = [u for t in range(0,len(T))]
        U1 = [u1 for t in range(0,len(T))]
        T, yout, xout = control.forced_response(sys_CL,T,U1,X0w1)
    if selu == 'sinusoidna funkcija':
        U = u*numpy.sin(2*numpy.pi/period*T)
        U1 = u1*numpy.sin(2*numpy.pi/period*T)
        T, yout, xout = control.forced_response(sys_CL,T,U1,X0w1)
    if selu == 'kvadratni val':
        U = u*numpy.sign(numpy.sin(2*numpy.pi/period*T))
        U1 = u1*numpy.sign(numpy.sin(2*numpy.pi/period*T))
        T, yout, xout = control.forced_response(sys_CL,T,U1,X0w1)
    # N.B. i primi 3 stati di xout sono quelli del sistema, mentre gli ultimi 3 sono quelli dell'osservatore

    step_info_dict = control.step_info(sys_CL[0,0],SettlingTimeThreshold=0.05,T=T)
    print('Informacija o odzivu: \n\tČas vzpona [s] =',step_info_dict['RiseTime'],'\n\tČas ustalitve [s] =',step_info_dict['SettlingTime'],'\n\tPrenihaj [%]=',step_info_dict['Overshoot'])
    print('Maksimalna vrednost x2 (%)=', max(abs(yout[C.shape[0]+2*B.shape[1]+1+1]))/(numpy.pi/180*30)*100)

    fig = plt.figure(num='Simulacija 1', figsize=(14,12))

    fig.add_subplot(221)
    plt.title('Odziv sistema')
    plt.ylabel('Izhod')
    plt.plot(T,yout[0],T,U,'r--')
    plt.xlabel('$t$ [s]')
    plt.axvline(x=0,color='black',linewidth=0.8)
    plt.axhline(y=0,color='black',linewidth=0.8)
    plt.legend(['$y$','referenca'])
    plt.grid()

    fig.add_subplot(222)
    plt.title('Vhod')
    plt.ylabel('$u$')
    plt.plot(T,yout[C.shape[0]])
    plt.xlabel('$t$ [s]')
    plt.axvline(x=0,color='black',linewidth=0.8)
    plt.axhline(y=0,color='black',linewidth=0.8)
    plt.grid()

    fig.add_subplot(223)
    plt.title('Odzive stanj')
    plt.ylabel('Stanja')
    plt.plot(T,yout[C.shape[0]+2*B.shape[1]+1],
             T,yout[C.shape[0]+2*B.shape[1]+1+1],
             T,yout[C.shape[0]+2*B.shape[1]+1+2],
             T,[numpy.pi/180*30 for i in range(len(T))],'r--',
             T,[-numpy.pi/180*30 for i in range(len(T))],'r--')
    plt.xlabel('$t$ [s]')
    plt.legend(['$x_{1}$','$x_{2}$','$x_{3}$','limit +$x_{2}$','limit -$x_{2}$'])
    plt.axvline(x=0,color='black',linewidth=0.8)
    plt.axhline(y=0,color='black',linewidth=0.8)
    plt.grid()

    fig.add_subplot(224)
    plt.title('Ocena napake stanj')
    plt.ylabel('Ocena napake stanj')
    plt.plot(T,yout[C.shape[0]+2*B.shape[1]+1+2]-yout[C.shape[0]+B.shape[1]])
    plt.xlabel('$t$ [s]')
    plt.legend(['$e_{3}$'])
    plt.axvline(x=0,color='black',linewidth=0.8)
    plt.axhline(y=0,color='black',linewidth=0.8)
    plt.grid()
    #plt.tight_layout()

alltogether2 = widgets.VBox([widgets.HBox([selm,
                                          sele,
                                          selu]),
                            widgets.Label(' ',border=3),
                            widgets.HBox([widgets.Label('K:',border=3), Kw,
                                          widgets.Label(' ',border=3),
                                          widgets.Label(' ',border=3),
                                          widgets.Label('Lastne vrednosti:',border=3),
                                          eig1c,
                                          eig2c,
                                          eig3c,
                                          widgets.Label(' ',border=3),
                                          widgets.Label(' ',border=3)]),
                            widgets.Label('X0 est.:',border=3), X0w,
                            widgets.Label(' ',border=3),
                            widgets.HBox([widgets.Label('L:',border=3), Lw,
                                          widgets.Label(' ',border=3),
                                          widgets.Label(' ',border=3),
                                          widgets.Label('Lastne vrednosti:',border=3),
                                          eig2o,
                                          eig3o,
                                          widgets.Label(' ',border=3),
                                          widgets.VBox([widgets.Label('Inverzno ojačanje vhoda:',border=3),
                                                        widgets.Label('Simulacijski čas (s):',border=3)]),
                                          widgets.VBox([gain_w2,simTime])]),
                            widgets.Label(' ',border=3),
                            widgets.HBox([u,
                                          period,
                                          START])])
out2 = widgets.interactive_output(main_callback2, {'Aw':Aw, 'Bw':Bw, 'X0w':X0w, 'K':Kw, 'L':Lw,
                                                 'eig1c':eig1c, 'eig2c':eig2c, 'eig3c':eig3c, 'eig2o':eig2o, 'eig3o':eig3o,
                                                 'u':u, 'period':period, 'selm':selm, 'sele':sele, 'selu':selu, 'simTime':simTime, 'DW':DW})
out2.layout.height = '870px'
display(out2, alltogether2)
```

    Output(layout=Layout(height='870px'))

    VBox(children=(HBox(children=(Dropdown(index=1, options=('Nastavi K in L', 'Nastavi lastne vrednosti'), value=…

```python

```


```python
# Erasmus+ ICCT project (2018-1-SI01-KA203-047081)

# Toggle cell visibility

from IPython.display import HTML
tag = HTML('''
Toggle cell visibility <a href="javascript:code_toggle()">here</a>.''')
display(tag)

# Hide the code completely

# from IPython.display import HTML
# tag = HTML('''<style>
# div.input {
#     display:none;
# }
# </style>''')
# display(tag)

```

Toggle cell visibility here.

## Notranja stabilnost

Koncept notranje stabilnosti je vezan na odziv sistema, ki ga odklonimo iz ravnotežnega stanja; opisuje ali se stanje sistema po motnji razlikuje od stanja sistema pred motnjo (tj. v ravnotežju).

### Definicija
Za časovno nespremenljiv dinamični sistem, ki ga popisujejo vektor stanja $x(t)\in \mathbb{R}^n$, ravnotežna točka $x_e$, začetno stanje $x_0$ ter začetni čas $t_0$, mora veljati:

$$
\forall \, \epsilon \in \mathbb{R}, \, \epsilon > 0 \quad \exists \delta \in \mathbb{R}, \, \delta > 0 : \quad ||x_0-x_e|| < \delta \, \Rightarrow  \, ||x(t)-x_e|| < \epsilon \quad \forall t \ge t_0.
$$

Zgornji zapis lahko preberemo na naslednji način: če obstaja dovolj majhna motnja $\delta$, ki odkloni sistem iz ravnotežne točke in povzroči, da se odziv $x(t)$ ne razlikuje dovolj od ravnotežne točke (tj. za ne več kot $\epsilon$), potem rečemo, da je ravnotežna točka stabilna.

Dodatno, če velja $\lim_{t\to\infty}||x(t)-x_e|| = 0$, lahko to preberemo kot: če se sistem vrne nazaj v ravnotežno točko, potem rečemo, da je ravnotežje asimptotično stabilno.

V primeru linearnega časovno nespremenljivega sistema:

\begin{cases}
\dot{x} = Ax +Bu \\
y = Cx + Du,
\end{cases}

lahko dokažemo, da stabilnost ene ravnotežne točke sistema nakazuje tudi stabilnost preostalih ravnotežnih točk; tako lahko govorimo o stabilnosti sistema (v splošnem) tudi kadar imamo na voljo informacijo o stabilnosti zgolj posamezne ravnotežne točke. Ta posebnost linearnih sistemov je posledica dejstva, da je odziv teh sistemov strogo povezan z lastnimi vrednostmi dinamične matrike $A$, ki so rotacijsko, translacijsko, časovno, in z ozirom na začetne pogoje nespremenljive.

V interaktivnem primeru [Modalna analiza](SS-02-Modalna_analiza.ipynb) je navedeno:

> Rešitev diferencialne enačbe na podlagi začetnega časa $t_0$ in začetnih pogojev $x(t_0)$ je:
$$
x(t) = e^{A(t-t_0)}x(t_0).
$$ Matrika $e^{A(t-t_0)}x(t_0)$ je linearna kombinacija časovnih funkcij $t$, od katerih je vsaka posamezna oblike $$e^{\lambda t},$$ kjer $\lambda$ označuje lastne vrednosti matrike $A$; te funkcija imenujemo modalne oblike sistema.

Iz tega sledi:
- linearni dinamični sistem je stabilen če in samo če vse njegove modalne oblike niso konvergentne,
- linearni dinamični sistem je stabilen če in samo če so vse njegove modalne oblike konvergentne,
- linearni dinamični sistem je nestabilen, če ima vsaj eno divergentno modalno obliko.

Z ozirom na lastne vrednosti dinamične matrike lahko zgornje pogoje zapišemo kot:
- Vse lastne vrednosti matrike $A$ ležijo v zaprti levi polovici kompleksne ravnine (tj. imajo realni del negativen ali enak 0) in je v primeru ničelnega realnega dela njihova algebraična večkratnost enaka geometrični oz. so elementi Jordanovih blokov skalarji.
- Vse lastne vrednosti pripadajo odprti levi polovici kompleksne ravnine, kar pomeni da imajo negativen realni del.
- Najmanj ena lastna vrednost ima pozitiven realni del, ali pa imajo lastne vrednosti ničelni realni del in Jordanove bloke, katerih elementi niso skalarji.

Ta interaktivni primer prikazuje dinamično matriko $A$, ki prikazuje njen prosti odziv ter pripadajoče lastne vrednosti.

### Kako upravljati s tem interaktivnim primerom?
- Opazuj odziv sistema ob spremembah lastnih vrednosti in začetnih pogojev $x_0$.

```python
%matplotlib inline
#%matplotlib notebook
import control as control
import numpy
import sympy as sym
from IPython.display import display, Markdown
import ipywidgets as widgets
import matplotlib.pyplot as plt

#print a matrix latex-like
def bmatrix(a):
     """Returns a LaTeX bmatrix - by Damir Arbula (ICCT project)

     :a: numpy array
     :returns: LaTeX bmatrix as a string
     """
     if len(a.shape) > 2:
         raise ValueError('bmatrix can at most display two dimensions')
     lines = str(a).replace('[', '').replace(']', '').splitlines()
     rv = [r'\begin{bmatrix}']
     rv += ['  ' + ' & '.join(l.split()) + r'\\' for l in lines]
     rv +=  [r'\end{bmatrix}']
     return '\n'.join(rv)

# Display formatted matrix:
def vmatrix(a):
    if len(a.shape) > 2:
         raise ValueError('bmatrix can at most display two dimensions')
    lines = str(a).replace('[', '').replace(']', '').splitlines()
    rv = [r'\begin{vmatrix}']
    rv += ['  ' + ' & '.join(l.split()) + r'\\' for l in lines]
    rv +=  [r'\end{vmatrix}']
    return '\n'.join(rv)

#matrixWidget is a matrix looking widget built with a VBox of HBox(es) that returns a numPy array as value !
class matrixWidget(widgets.VBox):
    def updateM(self,change):
        for irow in range(0,self.n):
            for icol in range(0,self.m):
                self.M_[irow,icol] = self.children[irow].children[icol].value
                #print(self.M_[irow,icol])
        self.value = self.M_

    def dummychangecallback(self,change):
        pass

    def __init__(self,n,m):
        self.n = n
        self.m = m
        self.M_ = numpy.matrix(numpy.zeros((self.n,self.m)))
        self.value = self.M_
        widgets.VBox.__init__(self,
                             children = [
                                 widgets.HBox(children =
                                              [widgets.FloatText(value=0.0, layout=widgets.Layout(width='90px')) for i in range(m)]
                                             )
                                 for j in range(n)
                             ])

        #fill in widgets and tell interact to call updateM each time a children changes value
        for irow in range(0,self.n):
            for icol in range(0,self.m):
                self.children[irow].children[icol].value = self.M_[irow,icol]
                self.children[irow].children[icol].observe(self.updateM, names='value')
        #value = Unicode('example@example.com', help="The email value.").tag(sync=True)
        self.observe(self.updateM, names='value', type= 'All')

    def setM(self, newM):
        #disable callbacks, change values, and reenable
        self.unobserve(self.updateM, names='value', type= 'All')
        for irow in range(0,self.n):
            for icol in range(0,self.m):
                self.children[irow].children[icol].unobserve(self.updateM, names='value')
        self.M_ = newM
        self.value = self.M_
        for irow in range(0,self.n):
            for icol in range(0,self.m):
                self.children[irow].children[icol].value = self.M_[irow,icol]
        for irow in range(0,self.n):
            for icol in range(0,self.m):
                self.children[irow].children[icol].observe(self.updateM, names='value')
        self.observe(self.updateM, names='value', type= 'All')

                #self.children[irow].children[icol].observe(self.updateM, names='value')

#overlaod class for state space systems that DO NOT remove "useless" states (what "professor" of automatic control would do this?)
class sss(control.StateSpace):
    def __init__(self,*args):
        #call base class init constructor
        control.StateSpace.__init__(self,*args)
    #disable function below in base class
    def _remove_useless_states(self):
        pass
```

```python
# Preparatory cell

A = numpy.matrix([[0,1],[-2/5,-1/5]])
X0 = numpy.matrix('5; 3')

Aw = matrixWidget(2,2)
Aw.setM(A)
X0w = matrixWidget(2,1)
X0w.setM(X0)
```

```python
# Misc

#create dummy widget
DW = widgets.FloatText(layout=widgets.Layout(width='0px', height='0px'))

#create button widget
START = widgets.Button(
    description='Test',
    disabled=False,
    button_style='', # 'success', 'info', 'warning', 'danger' or ''
    tooltip='Test',
    icon='check'
)

def on_start_button_clicked(b):
    #This is a workaround to have intreactive_output call the callback:
    #   force the value of the dummy widget to change
    if DW.value> 0 :
        DW.value = -1
    else:
        DW.value = 1
    pass
START.on_click(on_start_button_clicked)
```

```python
# Main cell

def main_callback(A, X0, DW):
    sols = numpy.linalg.eig(A)
    sys = sss(A,[[1],[0]],[0,1],0)
    pole = control.pole(sys)
    if numpy.real(pole[0]) != 0:
        p1r = abs(numpy.real(pole[0]))
    else:
        p1r = 1
    if numpy.real(pole[1]) != 0:
        p2r = abs(numpy.real(pole[1]))
    else:
        p2r = 1
    if numpy.imag(pole[0]) != 0:
        p1i = abs(numpy.imag(pole[0]))
    else:
        p1i = 1
    if numpy.imag(pole[1]) != 0:
        p2i = abs(numpy.imag(pole[1]))
    else:
        p2i = 1

    print('Lastni vrednosti matrike A sta:',round(sols[0][0],4),'in',round(sols[0][1],4))

    #T = numpy.linspace(0, 60, 1000)
    T, yout, xout = control.initial_response(sys,X0=X0,return_x=True)

    fig = plt.figure("Lastni vrednosti matrike A sta:", figsize=(16,16))
    ax = fig.add_subplot(311,title='Poli (realni vs imaginarni)')
    #plt.axis(True)
    # Move left y-axis and bottim x-axis to centre, passing through (0,0)
    # Eliminate upper and right axes
    ax.spines['left'].set_position(('data',0.0))
    ax.spines['bottom'].set_position(('data',0.0))
    ax.spines['right'].set_color('none')
    ax.spines['top'].set_color('none')
    ax.set_xlim(-max([p1r+p1r/3,p2r+p2r/3]),
                max([p1r+p1r/3,p2r+p2r/3]))
    ax.set_ylim(-max([p1i+p1i/3,p2i+p2i/3]),
                max([p1i+p1i/3,p2i+p2i/3]))

    plt.plot([numpy.real(pole[0]),numpy.real(pole[1])],[numpy.imag(pole[0]),numpy.imag(pole[1])],'o')
    plt.grid()

    ax1 = fig.add_subplot(312,title='Prosti odziv')
    plt.plot(T,xout[0])
    plt.grid()
    ax1.set_xlabel('čas [s]')
    ax1.set_ylabel('$x_1$')
    ax1.axvline(x=0,color='black',linewidth='0.8')
    ax1.axhline(y=0,color='black',linewidth='0.8')
    ax2 = fig.add_subplot(313)
    plt.plot(T,xout[1])
    plt.grid()
    ax2.set_xlabel('čas [s]')
    ax2.set_ylabel('$x_2$')
    ax2.axvline(x=0,color='black',linewidth='0.8')
    ax2.axhline(y=0,color='black',linewidth='0.8')

    #plt.show()

alltogether = widgets.HBox([widgets.VBox([widgets.Label('$A$:',border=3),
                                          Aw]),
                                          widgets.Label('   ',border=3),
                            widgets.VBox([widgets.Label('$X_0$:',border=3),
                                          X0w]),
                                          START])
out = widgets.interactive_output(main_callback, {'A':Aw, 'X0':X0w, 'DW':DW})
out.layout.height = '1000px'
display(out, alltogether)
```

    Output(layout=Layout(height='1000px'))

    HBox(children=(VBox(children=(Label(value='$A$:'), matrixWidget(children=(HBox(children=(FloatText(value=0.0, …