# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# # Funciones y expresiones booleanas

# El paquete `sympy` tiene un módulo de [lógica](http://docs.sympy.org/latest/modules/logic.html). Con él podemos hacer algunas simplificaciones

from sympy import *

# Definimos los símbolos que vamos a utilizar. Supremo e ínfimo se introducen usando el o e y lógicos. Aunque también podemos utilizar `And` y `Or` como funciones. Para la negación usamos `Not` o `~`. Tenemos además `Xor`, `Nand`, `Implies` (que se puede usar de forma prefija con `>>`) y `Equivalent`.

x, y, z = symbols("x,y,z")

p = (x | y) & ~ z

pprint(p)

# La formas normales conjuntiva y disyuntivas las podemos calcular como sigue

to_cnf(p)

to_dnf(p)

# También podemos simplificar expresiones

simplify(x | ~x)

# O dar valores de verdad a las variables

p.xreplace({x:True})

# Esto nos permite crear nuestras propias tablas de verdad

p.free_symbols

p = Or(x,And(x,y))

from IPython.display import HTML,display


colores=['LightCoral','Aquamarine']
tabla="<table style='width:25%'><tr><td bgcolor='lightblue'>$"+latex(x)
tabla=tabla+"$ </td><td bgcolor='lightblue'>$"+latex( y)+"$</td><td bgcolor='lightblue'>$"+latex(p)+"$</td></tr>"
for t in cartes({True,False}, repeat=2):
    v =dict(zip((x,y),t))
    tabla=tabla+"<tr> <td bgcolor="+colores[v[x]]+">"+str(v[x])
    tabla=tabla+"</td><td bgcolor="+colores[v[y]]+">"+str(v[y])
    tabla=tabla+"</td><td bgcolor="+colores[v[x]]+">"+str(p.xreplace(v))+"</td></tr>"
tabla=tabla+"</table>"
display(HTML(tabla))

# Una forma de comprobar que dos expresiones son equivalentes es la siguiente

Equivalent(simplify(p), simplify(x))

# Veamos ahora cómo podemos encontrar la versión simplificada de una función booleana que venga dada por minterms. Aparentemente `SOPform` hace algunas simplificaciones usando el algoritmo de Quine-McCluskey

p=SOPform([x,y,z],[[0,0,1],[0,1,0],[0,1,1],[1,1,0],[1,0,0],[1,0,1]])

p

# Al utilizar `sympy` podemos escribir una forma más amigable de una expresión booleana

pprint(p)

# Los comandos `simplify` or `simplify_logic` pueden simplificar aún más

pprint(simplify(p))

pprint(simplify_logic(p))

# De hecho, `p` se puede escribir de forma más compacta. Para ello vamos a utilizar el algoritmo espresso, que viene implementado en el paquete `pyeda`

from pyeda.inter import *

# Este paquete no admite las variables definidas con `symbols`, así que las vamos a declarar con `expvar` para definir variables booleanas

x,y,z = map(exprvar,"xyz")

p=SOPform([x,y,z],[[0,0,1],[0,1,0],[0,1,1],[1,1,0],[1,0,0],[1,0,1]])

# Otro problema es que la salida de `SOPform` no es una expresión de `pyeda`. Lo podemos arreglar pasándola a cadena de caracteres y releyéndola en `pyeda`

p=expr(str(p))

# Ahora sí que podemos utilizar el simplificador *espresso* implementado en `pyeda`

pm, =espresso_exprs(p)

pm

# Y podemos comprobar que es más corta que la salida que daba `sympy`. Para escribirla de forma más "legible" volvemos a utilizar `pprint` de `sympy`, pero para ello necesitamos pasar nuestra expresión en `pyeda` a `sympy`

pprint(sympify(pm))

# Podríamos haber definido directamente `p` utilizando tablas de verdad

p=truthtable([x,y,z], "01111110")

pm, = espresso_tts(p)

pprint(sympify(pm))

# La tabla de verdad de una expresión se obtiene como sigue

expr2truthtable(pm)

# Veamos un ejemplo análogo pero con más variables, y de paso mostramos como definir vectores de variables 

X = ttvars('x', 4)
f = truthtable(X, "0111111111111110")

fm, = espresso_tts(f)

fm

expr2truthtable(fm)

# ### Un ejemplo de simplificación

# Veamos que el o exclusivo con la definición $x\oplus y=(x\wedge \neg y)\vee (\neg x\wedge y)$ es asociativo

x, y, z = map(exprvar,"xyz")

f = lambda x,y : Or(And(x,~ y),And(~x,y))

f(x,y)

expr2truthtable(f(x,y))

f(x,y).equivalent(Xor(x,y))

# Veamos que efectivamente $x\oplus(y\oplus z)=(x\oplus y)\oplus z$

pprint(simplify_logic(f(x,f(y,z))))

pprint(simplify_logic(f(f(x,y),z)))

a= f(f(x,y),z)
b= f(x,f(y,z))

a.equivalent(b)


# Podemos hacer una función que pase de minterm a expresions en pyeda

def minterm2expr(l,v):
    n = len(l)
    vv=v.copy()
    for i in range(n):
        if not(l[i]):
            vv[i]=Not(vv[i])
    return And(*vv)


x,y,z,t = map(exprvar,"xyzt")

minterm2expr([0,1,0,1],[x,y,z,t])


def minterms2expr(l,v):
    return Or(*[minterm2expr(a,v) for a in l])


hh2=minterms2expr([[0,0,0,0],[0,0,1,0],[0,1,0,0],[0,1,1,0],[0,1,1,1],[1,0,0,0],[1,0,1,0],[1,1,0,0]],[x,y,z,t])

hh2

pprint(sympify(hh2))

# Y ahora la podemos simplificar

sh2, = espresso_exprs(hh2)

sh2

StarcoderdataJupyterScriptsDedupFiltered