pyDatalog é uma biblioteca que implementa programação lógica. Ela é inspirada em Prolog, mas usa sintaxe Python. Primeiramente, você declara fatos sobre o mundo. Depois, define regras que derivam novos fatos. Por fim, faz consultas para extrair informações. Isso é muito diferente da programação imperativa comum. Em vez de dizer “como” fazer, você diz “o que” é verdade. O motor de inferência encontra todas as soluções. Além disso, pyDatalog suporta operações aritméticas. Também permite agregações como contagem e soma. Portanto, é uma ferramenta para problemas baseados em regras. Assim, você resolve problemas complexos de forma elegante.
Conceitos fundamentais do pyDatalog
Três conceitos principais formam o pyDatalog.
Fatos são afirmações verdadeiras e indivisíveis.
Por exemplo, +pai('joao', 'maria') é um fato.
Regras definem relacionamentos condicionais.
Elas usam o operador <= para implicação.
Por exemplo, avo(X,Z) <= pai(X,Y) & pai(Y,Z).
Consultas perguntam ao sistema por verdades.
Use print(pai(X, 'maria')) para achar pais.
Primeiramente, todos símbolos devem ser declarados.
Use pyDatalog.create_terms() para isso.
Além disso, pyDatalog usa avaliação de curto-circuito.
Isso foi inspirado na resolução lógica de Robinson.
Portanto, o sistema é completo e consistente.
Uma fórmula que representa uma regra é:
Quando utilizar pyDatalog no seu projeto
Use pyDatalog para sistemas baseados em regras. Sistemas especialistas de diagnóstico são ideais. Também para motores de recomendação personalizados. Árvores genealógicas e grafos de parentesco são clássicos. Outro bom uso é para validação de regras de negócio. Por exemplo, regras de aprovação de crédito. Além disso, use para problemas de caminho em grafos. O pyDatalog encontra todas as rotas possíveis. Evite pyDatalog para cálculos numéricos pesados. Também não é adequado para milhões de fatos. Primeiramente, modele seu domínio com poucos dados. Depois, escale gradualmente se necessário. Portanto, avalie o tamanho do seu problema antes.
Outro caso de uso é em jogos baseados em lógica. Quebra-cabeças como Sudoku ou 8-rainhas. Você declara as regras e o pyDatalog resolve. Isso foi demonstrado em competições de IA. Use também para validação de pré-requisitos educacionais. “Aluno pode cursar P2 se aprovado em P1”. Assim, você centraliza a lógica de negócio. Portanto, pyDatalog é uma ferramenta versátil.
Exemplo prático: árvore genealógica e regras
O código abaixo implementa um sistema de parentesco. Definimos fatos sobre uma família típica. Regras estabelecem relações como avô e irmão. Consultas encontram parentes por inferência. Observe como nenhum loop foi escrito manualmente. O pyDatalog gerencia toda a recursão necessária. Isso é o poder da programação declarativa. Vamos ao código comentado para demonstração.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# Instalação: pip install pyDatalog from pyDatalog import pyDatalog # ============================================ # Passo 1: Declarar termos (símbolos) # ============================================ pyDatalog.create_terms( 'pai, mae, homem, mulher, filho, filha', 'avo, avo_paterno, avo_materno, irmao, irma', 'ancestral, descendente, primo', 'X, Y, Z, W' ) # ============================================ # Passo 2: Inserir fatos (base de conhecimento) # ============================================ # Relações de parentesco direto +pai('joao', 'maria') +pai('joao', 'jose') +pai('jose', 'lucas') +pai('jose', 'sofia') +pai('carlos', 'ana') +pai('carlos', 'paulo') +pai('paulo', 'rafa') +mae('maria', 'ana') +mae('maria', 'paulo') +mae('ana', 'sofia') +mae('ana', 'lucas') +mae('lucia', 'jose') +mae('lucia', 'maria') # Gêneros (para regras como irmão vs irmã) +homem('joao') +homem('jose') +homem('carlos') +homem('paulo') +homem('lucas') +homem('rafa') +mulher('maria') +mulher('ana') +mulher('lucia') +mulher('sofia') # ============================================ # Passo 3: Definir regras (lógica derivada) # ============================================ # Regra: avô (pai do pai ou pai da mãe) avo(X, Z) <= pai(X, Y) & pai(Y, Z) avo(X, Z) <= pai(X, Y) & mae(Y, Z) # Regra: avô paterno (pai do pai) avo_paterno(X, Z) <= pai(X, Y) & pai(Y, Z) # Regra: avô materno (pai da mãe) avo_materno(X, Z) <= pai(X, Y) & mae(Y, Z) # Regra: irmão (mesmo pai ou mesma mãe, diferente gênero) irmao(X, Y) <= (pai(Z, X) & pai(Z, Y) | mae(Z, X) & mae(Z, Y)) & (X != Y) & homem(Y) # Regra: irmã (mesmo pai ou mesma mãe, diferente gênero) irma(X, Y) <= (pai(Z, X) & pai(Z, Y) | mae(Z, X) & mae(Z, Y)) & (X != Y) & mulher(Y) # Regra: ancestral (recursiva) ancestral(X, Y) <= pai(X, Y) ancestral(X, Y) <= mae(X, Y) ancestral(X, Y) <= pai(X, Z) & ancestral(Z, Y) ancestral(X, Y) <= mae(X, Z) & ancestral(Z, Y) # Regra: descendente (inverso do ancestral) descendente(Y, X) <= ancestral(X, Y) # Regra: primo (primos de primeiro grau) primo(X, Y) <= pai(A, X) & pai(B, Y) & irmao(A, B) primo(X, Y) <= pai(A, X) & mae(B, Y) & irmao(A, B) primo(X, Y) <= mae(A, X) & pai(B, Y) & irmao(A, B) primo(X, Y) <= mae(A, X) & mae(B, Y) & irmao(A, B) # ============================================ # Passo 4: Consultas # ============================================ print("=== Consultas Familiares ===") print("\n1. Quem é avô de ana?") print(avo(X, 'ana')) print("\n2. Quem é avô paterno de lucas?") print(avo_paterno(X, 'lucas')) print("\n3. Quem é avô materno de sofia?") print(avo_materno(X, 'sofia')) print("\n4. Irmãos de ana:") print(irmao(X, 'ana')) print("\n5. Irmãs de jose:") print(irma(X, 'jose')) print("\n6. Antepassados de lucas:") print(ancestral(X, 'lucas')) print("\n7. Descendentes de joao:") print(descendente(X, 'joao')) print("\n8. Primos de ana:") print(primo(X, 'ana')) print("\n9. Todos os avôs e netos:") print(avo(X, Y)) # ============================================ # Passo 5: Exemplo com aritmética e agregação # ============================================ print("\n=== Aritmética e Agregação ===") pyDatalog.create_terms('idade, total_idades, media_idade, N, Soma, Media') # Fatos com idades +idade('joao', 70) +idade('maria', 65) +idade('jose', 45) +idade('ana', 40) +idade('lucas', 15) +idade('sofia', 12) # Regra para somar idades (agregação) total_idades(Soma) <= (Soma == sum_(idade(Y, N), for_each=N)) # Regra para calcular média media_idade(Media) <= (Media == (sum_(idade(Y, N), for_each=N)) / (count_(idade(Y, N), for_each=N))) print("Idades registradas:") print(idade(X, Y)) print("\nSoma total das idades:") print(total_idades(Soma)) print("\nMédia de idade:") print(media_idade(Media)) # ============================================ # Passo 6: Resolvendo um quebra-cabeça lógico # ============================================ print("\n=== Quebra-cabeça: Quem é o assassino? ===") pyDatalog.create_terms('assassino, mentiroso, honesto, suspeito') # Fatos: quem diz o quê # Suspeitos: A, B, C, D # A diz: "B é o assassino" # B diz: "C é o assassino" # C diz: "D é o assassino" # D diz: "Eu não sou o assassino" # Apenas um é honesto, os outros mentem # Regra: mentiroso se a afirmação é falsa mentiroso('A') <= (assassino('B') != True) mentiroso('B') <= (assassino('C') != True) mentiroso('C') <= (assassino('D') != True) mentiroso('D') <= (assassino('D') == True) # Regra: honesto se não é mentiroso honesto(X) <= ~mentiroso(X) # Restrição: exatamente um honesto # (implementado pelo número de soluções para honesto) # Tentar cada possível assassino for sus in ['A', 'B', 'C', 'D']: # Limpar fatos anteriores pyDatalog.clear() pyDatalog.create_terms('assassino, mentiroso, honesto, X') # Afirmar quem é o assassino +assassino(sus) # Recriar regras mentiroso('A') <= (assassino('B') != True) mentiroso('B') <= (assassino('C') != True) mentiroso('C') <= (assassino('D') != True) mentiroso('D') <= (assassino('D') == True) honesto(X) <= ~mentiroso(X) # Contar honestos honestos = list(honesto(X)) if len(honestos) == 1: print(f"Solução encontrada! Assassino é {sus}") print(f"Honesto: {honestos[0][0]}") print("\nNota: Este é um exemplo didático de inferência lógica.") |
No código, a base de conhecimento é declarativa.
Nenhum loop ou condicional foi escrito manualmente.
O pyDatalog unifica variáveis e resolve recursões.
Primeiramente, declare todos os termos usados.
Depois, insira fatos com o operador +.
Regras usam <= para implicação lógica.
Consultas são feitas com expressões simples.
O retorno é uma lista de tuplas com soluções.
Além disso, o pyDatalog suporta negação com ~.
Isso é útil para regras do tipo “não é verdade que”.
Agregações como sum_ e count_ são suportadas.
Portanto, você tem um sistema lógico completo.
Outro ponto importante é a recursão em regras.
A regra ancestral é recursiva e funciona.
O pyDatalog detecta automaticamente a recursão.
Isso foi inspirado em bancos de dados dedutivos.
Cuidado com recursão infinita em regras mal formuladas.
Sempre tenha um caso base que não seja recursivo.
Além disso, a performance piora com profundidade extrema.
Para árvores muito profundas, considere outras soluções.
Primeiramente, teste com dados pequenos e consistentes.
Depois, expanda gradualmente o conhecimento.
Assim, você evita surpresas de desempenho.
Portanto, pyDatalog é robusto para problemas bem modelados.
✅ Fatos: conhecimento base (+fato)
✅ Regras: implicações (conclusão <= condição)
✅ Consultas: perguntas ao sistema (fato(X,Y))
✅ Agregações: sum_, count_, min_, max_
✅ Aritmética: operadores matemáticos padrão
Finalmente, explore exemplos da documentação oficial. O pyDatalog é mantido ativo e bem documentado. Use para prototipar sistemas especialistas rapidamente. Também para ensinar programação lógica em Python. A curva de aprendizado é suave para iniciantes. No entanto, problemas muito grandes podem exigir otimização. Nesses casos, exporte fatos para banco de dados. Use pyDatalog para a camada de regras apenas. Assim, você combina escalabilidade com expressividade. Portanto, pyDatalog merece um lugar no seu toolkit. Instale e comece a programar logicamente hoje mesmo. Você descobrirá um novo paradigma encantador.