AI com Python - Programação Lógica
Neste capítulo, enfocaremos a programação lógica e como ela ajuda na Inteligência Artificial.
Já sabemos que a lógica é o estudo dos princípios do raciocínio correto ou, em palavras simples, é o estudo do que vem depois do quê. Por exemplo, se duas afirmações são verdadeiras, podemos inferir qualquer terceira afirmação a partir delas.
Conceito
A programação lógica é a combinação de duas palavras, lógica e programação. A programação lógica é um paradigma de programação no qual os problemas são expressos como fatos e regras por declarações de programa, mas dentro de um sistema de lógica formal. Assim como outros paradigmas de programação, como orientado a objetos, funcional, declarativo e procedural, etc., também é uma forma particular de abordar a programação.
Como resolver problemas com programação lógica
A programação lógica usa fatos e regras para resolver o problema. É por isso que são chamados de blocos de construção da Programação Lógica. Uma meta precisa ser especificada para cada programa de programação lógica. Para entender como um problema pode ser resolvido na programação lógica, precisamos saber sobre os blocos de construção - Fatos e Regras -
Fatos
Na verdade, todo programa lógico precisa de fatos com os quais trabalhar, de modo que possa atingir o objetivo dado. Os fatos são basicamente afirmações verdadeiras sobre o programa e os dados. Por exemplo, Delhi é a capital da Índia.
Regras
Na verdade, as regras são as restrições que nos permitem tirar conclusões sobre o domínio do problema. Regras basicamente escritas como cláusulas lógicas para expressar vários fatos. Por exemplo, se estamos construindo qualquer jogo, todas as regras devem ser definidas.
As regras são muito importantes para resolver qualquer problema de programação em lógica. Regras são conclusões basicamente lógicas que podem expressar os fatos. A seguir está a sintaxe da regra -
A∶− B1, B2, ..., B n .
Aqui, A é a cabeça e B1, B2, ... Bn é o corpo.
Por exemplo - ancestral (X, Y): - pai (X, Y).
ancestral (X, Z): - pai (X, Y), ancestral (Y, Z).
Isso pode ser lido como, para cada X e Y, se X é o pai de Y e Y é um ancestral de Z, X é o ancestral de Z. Para cada X e Y, X é o ancestral de Z, se X for o pai de Y e Y é um ancestral de Z.
Instalando Pacotes Úteis
Para iniciar a programação lógica em Python, precisamos instalar os dois pacotes a seguir -
Kanren
Ele nos fornece uma maneira de simplificar a maneira como criamos código para lógica de negócios. Permite-nos expressar a lógica em termos de regras e fatos. O seguinte comando irá ajudá-lo a instalar o Kanren -
pip install kanren
SymPy
SymPy é uma biblioteca Python para matemática simbólica. Ele tem como objetivo se tornar um sistema de álgebra computacional (CAS) completo, mantendo o código o mais simples possível para ser compreensível e facilmente extensível. O seguinte comando ajudará você a instalar o SymPy -
pip install sympy
Exemplos de programação lógica
A seguir estão alguns exemplos que podem ser resolvidos por programação lógica -
Expressões matemáticas correspondentes
Na verdade, podemos encontrar os valores desconhecidos usando a programação lógica de uma forma muito eficaz. O seguinte código Python ajudará você a encontrar uma expressão matemática -
Considere importar os seguintes pacotes primeiro -
from kanren import run, var, fact
from kanren.assoccomm import eq_assoccomm as eq
from kanren.assoccomm import commutative, associative
Precisamos definir as operações matemáticas que vamos usar -
add = 'add'
mul = 'mul'
Tanto a adição quanto a multiplicação são processos comunicativos. Portanto, precisamos especificá-lo e isso pode ser feito da seguinte maneira -
fact(commutative, mul)
fact(commutative, add)
fact(associative, mul)
fact(associative, add)
É obrigatório definir variáveis; isso pode ser feito da seguinte maneira -
a, b = var('a'), var('b')
Precisamos combinar a expressão com o padrão original. Temos o seguinte padrão original, que é basicamente (5 + a) * b -
Original_pattern = (mul, (add, 5, a), b)
Temos as duas seguintes expressões para corresponder ao padrão original -
exp1 = (mul, 2, (add, 3, 1))
exp2 = (add,5,(mul,8,1))
A saída pode ser impressa com o seguinte comando -
print(run(0, (a,b), eq(original_pattern, exp1)))
print(run(0, (a,b), eq(original_pattern, exp2)))
Depois de executar este código, obteremos a seguinte saída -
((3,2))
()
A primeira saída representa os valores para a e b. A primeira expressão correspondeu ao padrão original e retornou os valores paraa e b mas a segunda expressão não correspondeu ao padrão original, portanto, nada foi retornado.
Verificando números primos
Com a ajuda da programação lógica, podemos encontrar os números primos de uma lista de números e também gerar números primos. O código Python fornecido a seguir encontrará o número primo em uma lista de números e também gerará os primeiros 10 números primos.
Vamos primeiro considerar a importação dos seguintes pacotes -
from kanren import isvar, run, membero
from kanren.core import success, fail, goaleval, condeseq, eq, var
from sympy.ntheory.generate import prime, isprime
import itertools as it
Agora, definiremos uma função chamada prime_check que verificará os números primos com base nos números fornecidos como dados.
def prime_check(x):
if isvar(x):
return condeseq([(eq,x,p)] for p in map(prime, it.count(1)))
else:
return success if isprime(x) else fail
Agora, precisamos declarar uma variável que será usada -
x = var()
print((set(run(0,x,(membero,x,(12,14,15,19,20,21,22,23,29,30,41,44,52,62,65,85)),
(prime_check,x)))))
print((run(10,x,prime_check(x))))
A saída do código acima será a seguinte -
{19, 23, 29, 41}
(2, 3, 5, 7, 11, 13, 17, 19, 23, 29)
Resolvendo quebra-cabeças
A programação lógica pode ser usada para resolver muitos problemas como quebra-cabeças de 8, quebra-cabeça de zebra, Sudoku, N-queen, etc.
There are five houses.
The English man lives in the red house.
The Swede has a dog.
The Dane drinks tea.
The green house is immediately to the left of the white house.
They drink coffee in the green house.
The man who smokes Pall Mall has birds.
In the yellow house they smoke Dunhill.
In the middle house they drink milk.
The Norwegian lives in the first house.
The man who smokes Blend lives in the house next to the house with cats.
In a house next to the house where they have a horse, they smoke Dunhill.
The man who smokes Blue Master drinks beer.
The German smokes Prince.
The Norwegian lives next to the blue house.
They drink water in a house next to the house where they smoke Blend.
Estamos resolvendo para a questão who owns zebra com a ajuda de Python.
Deixe-nos importar os pacotes necessários -
from kanren import *
from kanren.core import lall
import time
Agora, precisamos definir duas funções - left() e next() para verificar de quem é a casa restante ou ao lado da casa de quem -
def left(q, p, list):
return membero((q,p), zip(list, list[1:]))
def next(q, p, list):
return conde([left(q, p, list)], [left(p, q, list)])
Agora, vamos declarar uma casa variável da seguinte forma -
houses = var()
Precisamos definir as regras com a ajuda do pacote lall como segue.
Existem 5 casas -
rules_zebraproblem = lall(
(eq, (var(), var(), var(), var(), var()), houses),
(membero,('Englishman', var(), var(), var(), 'red'), houses),
(membero,('Swede', var(), var(), 'dog', var()), houses),
(membero,('Dane', var(), 'tea', var(), var()), houses),
(left,(var(), var(), var(), var(), 'green'),
(var(), var(), var(), var(), 'white'), houses),
(membero,(var(), var(), 'coffee', var(), 'green'), houses),
(membero,(var(), 'Pall Mall', var(), 'birds', var()), houses),
(membero,(var(), 'Dunhill', var(), var(), 'yellow'), houses),
(eq,(var(), var(), (var(), var(), 'milk', var(), var()), var(), var()), houses),
(eq,(('Norwegian', var(), var(), var(), var()), var(), var(), var(), var()), houses),
(next,(var(), 'Blend', var(), var(), var()),
(var(), var(), var(), 'cats', var()), houses),
(next,(var(), 'Dunhill', var(), var(), var()),
(var(), var(), var(), 'horse', var()), houses),
(membero,(var(), 'Blue Master', 'beer', var(), var()), houses),
(membero,('German', 'Prince', var(), var(), var()), houses),
(next,('Norwegian', var(), var(), var(), var()),
(var(), var(), var(), var(), 'blue'), houses),
(next,(var(), 'Blend', var(), var(), var()),
(var(), var(), 'water', var(), var()), houses),
(membero,(var(), var(), var(), 'zebra', var()), houses)
)
Agora, execute o solucionador com as restrições anteriores -
solutions = run(0, houses, rules_zebraproblem)
Com a ajuda do código a seguir, podemos extrair a saída do solucionador -
output_zebra = [house for house in solutions[0] if 'zebra' in house][0][0]
O código a seguir ajudará a imprimir a solução -
print ('\n'+ output_zebra + 'owns zebra.')
A saída do código acima seria a seguinte -
German owns zebra.