Doninha / syllogism_module.py
0danielfonseca's picture
Upload 42 files
cf52a55 verified
"""
MΓ“DULO β€” Silogismo CientΓ­fico AristotΓ©lico + Paradoxo de Hempel + Popper
=========================================================================
Integrado entre L2 e L3 (etapa 4 e 5 do fluxo).
Filtra as hipΓ³teses kantianas pelas 8 regras do silogismo cientΓ­fico e
aplica o princΓ­pio da falseabilidade: toda conclusΓ£o Γ© tratada como
FALSA atΓ© que se encontre evidΓͺncia verdadeira equivalente.
Paradoxo de Hempel implementado como filtro negativo:
Nem toda palavra posterior pode ser inferida da anterior.
Objetos irrelevantes nΓ£o validam uma teoria.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import List, Optional, Tuple
from l2_kantian_judgments import KantianJudgment
# ─────────────────────────────────────────────────────────────────────────────
# Estrutura de um silogismo
# ─────────────────────────────────────────────────────────────────────────────
@dataclass
class Syllogism:
major: str # premissa maior (universal)
minor: str # premissa menor (particular/singular)
conclusion: str # conclusΓ£o derivada
valid: bool = True
violations: List[str] = None
def __post_init__(self):
if self.violations is None:
self.violations = []
def __str__(self) -> str:
status = "βœ“ VÁLIDO" if self.valid else f"βœ— INVÁLIDO ({'; '.join(self.violations)})"
return (
f" Maior : {self.major}\n"
f" Menor : {self.minor}\n"
f" Concl.: {self.conclusion}\n"
f" Status: {status}"
)
# ─────────────────────────────────────────────────────────────────────────────
# As 8 regras do silogismo cientΓ­fico
# ─────────────────────────────────────────────────────────────────────────────
class AristotelianSyllogismValidator:
"""
Valida um silogismo segundo as 8 regras aristotΓ©licas e retorna
a lista de violaΓ§Γ΅es (vazia se vΓ‘lido).
"""
def validate(self, major: str, minor: str, conclusion: str) -> List[str]:
violations: List[str] = []
m_neg = self._is_negative(major)
n_neg = self._is_negative(minor)
c_neg = self._is_negative(conclusion)
m_part = self._is_particular(major)
n_part = self._is_particular(minor)
c_part = self._is_particular(conclusion)
# R1 β€” Apenas trΓͺs termos, cada um no mesmo sentido
terms_m = self._extract_key_terms(major)
terms_n = self._extract_key_terms(minor)
terms_c = self._extract_key_terms(conclusion)
all_terms = terms_m | terms_n | terms_c
if len(all_terms) > 6: # heurΓ­stica liberal
violations.append("R1: mais de trΓͺs termos distintos detectados")
# R2 β€” Termo mΓ©dio nΓ£o aparece na conclusΓ£o
middle = terms_m & terms_n - terms_c
if not middle and terms_m & terms_n:
violations.append("R2: termo mΓ©dio pode estar na conclusΓ£o")
# R3 β€” ConclusΓ£o nΓ£o excede extensΓ£o das premissas
if not c_part and (m_part or n_part):
violations.append("R3: conclusΓ£o mais extensa que as premissas")
# R4 β€” Termo mΓ©dio deve ser universal pelo menos uma vez
if m_part and n_part:
violations.append("R4: termo mΓ©dio nunca Γ© universal")
# R5 β€” De duas negativas, nada se conclui
if m_neg and n_neg:
violations.append("R5: duas premissas negativas β€” conclusΓ£o invΓ‘lida")
# R6 β€” Duas afirmativas β†’ conclusΓ£o afirmativa
if not m_neg and not n_neg and c_neg:
violations.append("R6: premissas afirmativas exigem conclusΓ£o afirmativa")
# R7 β€” De duas particulares, nada se conclui
if m_part and n_part:
violations.append("R7: duas premissas particulares β€” conclusΓ£o invΓ‘lida")
# R8 β€” "Parte Fraca": conclusΓ£o segue a premissa mais fraca
if (m_neg or n_neg) and not c_neg:
violations.append("R8: premissa negativa exige conclusΓ£o negativa")
if (m_part or n_part) and not c_part and not c_neg:
violations.append("R8: premissa particular exige conclusΓ£o particular")
return violations
# ── helpers ─────────────────────────────────────────────────────── #
@staticmethod
def _is_negative(text: str) -> bool:
neg_markers = {"nΓ£o", "nunca", "nenhum", "jamais", "nem", "negativo"}
return any(w in text.lower().split() for w in neg_markers)
@staticmethod
def _is_particular(text: str) -> bool:
part_markers = {"algum", "alguma", "alguns", "algumas", "certo",
"parte", "pode", "possΓ­vel"}
return any(w in text.lower().split() for w in part_markers)
@staticmethod
def _extract_key_terms(text: str) -> set:
stop = {"Γ©", "sΓ£o", "de", "do", "da", "em", "com", "por",
"para", "este", "esta", "esse", "toda", "todo", "um", "uma"}
import re
tokens = re.findall(r"[a-zÑàãÒéΓͺíóôáúüçA-ZΓΓ€ΓƒΓ‚Γ‰ΓŠΓΓ“Γ”Γ•ΓšΓœΓ‡]+", text.lower())
return {t for t in tokens if t not in stop and len(t) > 2}
# ─────────────────────────────────────────────────────────────────────────────
# Filtro de Hempel (anti-confirmaΓ§Γ£o espΓΊria)
# ─────────────────────────────────────────────────────────────────────────────
class HempelFilter:
"""
Paradoxo de Hempel: objetos irrelevantes nΓ£o devem confirmar hipΓ³teses.
Implementado como detecΓ§Γ£o de correlaΓ§Γ΅es espΓΊrias entre termos do
prompt e termos do banco de dados sem relaΓ§Γ£o semΓ’ntica real.
"""
def __init__(self, relevance_threshold: float = 0.25) -> None:
self.threshold = relevance_threshold
def is_spurious(self, judgment: KantianJudgment, prompt_terms: set) -> bool:
"""
Retorna True se a hipΓ³tese Γ© provavelmente espΓΊria
(confirmaΓ§Γ£o por objeto irrelevante).
"""
import re
hyp_terms = set(re.findall(
r"[a-zÑàãÒéΓͺíóôáúüçA-ZΓΓ€ΓƒΓ‚Γ‰ΓŠΓΓ“Γ”Γ•ΓšΓœΓ‡]+",
judgment.proposicao.lower()
))
overlap = len(hyp_terms & prompt_terms) / max(len(hyp_terms), 1)
return overlap < self.threshold # pouca sobreposiΓ§Γ£o = provΓ‘vel espΓΊrio
# ─────────────────────────────────────────────────────────────────────────────
# PrincΓ­pio da Falseabilidade de Popper
# ─────────────────────────────────────────────────────────────────────────────
class PopperFalsifiability:
"""
Toda conclusΓ£o Γ© tratada como FALSA atΓ© que se encontre evidΓͺncia
verdadeira equivalente no banco de dados.
Implementa o princΓ­pio do Cisne Negro: a proposiΓ§Γ£o universal
"todo cisne Γ© branco" Γ© falsa atΓ© que seja falsificada por um cisne preto.
"""
def __init__(self, falsifiability_floor: float = 0.1) -> None:
"""
falsifiability_floor : score mΓ­nimo de evidΓͺncia para aceitar
a hipΓ³tese como nΓ£o-falsificada.
"""
self.floor = falsifiability_floor
def apply(
self,
hypotheses: List[Tuple[KantianJudgment, float]], # (juΓ­zo, score_BD)
) -> List[Tuple[KantianJudgment, float, bool]]:
"""
Retorna triplas (juΓ­zo, score, falsificada?).
HipΓ³teses universais afirmativas partem sempre de score 0
(falsas atΓ© prova em contrΓ‘rio).
"""
result = []
for j, score in hypotheses:
# ProposiΓ§Γ΅es universais: presume falso atΓ© evidΓͺncia forte
if j.quantidade == "Universal":
adjusted = score if score >= self.floor else 0.0
falsified = adjusted < self.floor
# Singulares: usa o score direto
else:
adjusted = score
falsified = False
result.append((j, adjusted, falsified))
return result
# ─────────────────────────────────────────────────────────────────────────────
# Pipeline integrado
# ─────────────────────────────────────────────────────────────────────────────
class ScientificSyllogismPipeline:
"""
Integra: Silogismo AristotΓ©lico + Filtro de Hempel + Falseabilidade.
Chamado entre L2 e L3.
"""
def __init__(self) -> None:
self.validator = AristotelianSyllogismValidator()
self.hempel = HempelFilter()
self.popper = PopperFalsifiability()
def run(
self,
judgments: List[KantianJudgment],
prompt_terms: set,
kb_scores: dict, # termo_proposicao β†’ score [0,1]
) -> List[Tuple[KantianJudgment, float]]:
"""
Filtra e pontua as hipΓ³teses.
Retorna lista ordenada de (juΓ­zo, score_final).
"""
# 1. Remove hipΓ³teses espΓΊrias (Hempel)
non_spurious = [
j for j in judgments
if not self.hempel.is_spurious(j, prompt_terms)
]
# 2. Valida via silogismo (usa prioridade L2 como par maior/menor)
scored: List[Tuple[KantianJudgment, float]] = []
for j in non_spurious:
# ConstrΓ³i silogismo sintΓ©tico para validaΓ§Γ£o
major = f"Universal: {j.proposicao}"
minor = f"Singular: {j.proposicao}"
conclusion = j.proposicao
violations = self.validator.validate(major, minor, conclusion)
penalty = len(violations) * 0.1
base_score = kb_scores.get(j.proposicao[:30], j.prioridade)
scored.append((j, max(0.0, base_score - penalty)))
# 3. Aplica falseabilidade (Popper)
with_falsifiability = self.popper.apply(scored)
# 4. Remove falsificadas e reordena
valid = [
(j, score)
for j, score, falsified in with_falsifiability
if not falsified
]
valid.sort(key=lambda x: x[1], reverse=True)
return valid