File size: 10,401 Bytes
cf52a55 | 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 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | """
CAMADA L3 — Avaliação Paraconsistente
======================================
Implementa a Lógica Anotada de Evidências (LAE / PAL2v) de da Costa & Abe.
Cada proposição recebe um par de anotações:
μ ∈ [0,1] — grau de evidência FAVORÁVEL
λ ∈ [0,1] — grau de evidência CONTRÁRIA
Estados resultantes:
┌──────────────────────────────────────────────────┐
│ Verdadeiro : μ alto, λ baixo │
│ Falso : μ baixo, λ alto │
│ Inconsistente : μ alto, λ alto (contradição) │
│ Indeterminado : μ baixo, λ baixo │
│ Intermediário : valores médios (morno, etc.) │
└──────────────────────────────────────────────────┘
Princípio central:
"Contradição Local + Consistência Global ≠ Trivialização"
A explosão é GENTIL: uma contradição local (quente e frio) não
trivializa o sistema — produz o estado "Intermediário" (morno).
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Dict, List, Tuple, Optional
import math
import torch
try:
from paraconsistent_rules import (
state_from_rules,
state_12_to_simple,
load_rules_from_fuzzy_file,
ParaconsistentRules,
)
except Exception:
state_from_rules = None # type: ignore
state_12_to_simple = None # type: ignore
load_rules_from_fuzzy_file = None # type: ignore
ParaconsistentRules = None # type: ignore
try:
# Import opcional do modelo neural; o sistema continua funcional sem ele.
from neural_truth_model import TruthScoringModel, load_tokenizer, neural_annotations
except Exception: # pragma: no cover - fallback em ambientes sem transformers
TruthScoringModel = None # type: ignore
load_tokenizer = None # type: ignore
neural_annotations = None # type: ignore
# ─────────────────────────────────────────────────────────────────────────────
# Constantes de limiar
# ─────────────────────────────────────────────────────────────────────────────
THRESHOLD_TRUE = 0.7 # μ ≥ este valor e λ ≤ (1 - este) → Verdadeiro
THRESHOLD_FALSE = 0.3 # μ ≤ este e λ ≥ (1 - este) → Falso
THRESHOLD_INCONSISTENT = 0.6 # ambos acima → Inconsistente (contradição local)
THRESHOLD_INDETERMINATE= 0.4 # ambos abaixo → Indeterminado
@dataclass
class ParaconsistentValue:
"""
Valor-verdade paraconsistente para uma proposição.
Baseado na Lógica Anotada de Evidências (LAE).
"""
proposition: str
mu: float # evidência favorável ∈ [0,1]
lam: float # evidência contrária ∈ [0,1]
# ── Graus derivados ──────────────────────────────────────────────── #
@property
def certainty(self) -> float:
"""Grau de certeza: Gc = μ − λ ∈ [−1, 1]"""
return self.mu - self.lam
@property
def contradiction(self) -> float:
"""Grau de contradição: Gct = μ + λ − 1 ∈ [−1, 1]"""
return self.mu + self.lam - 1.0
@property
def state(self) -> str:
"""Estado lógico qualitativo. Usa regras do Fuzzy.txt se disponíveis."""
if state_from_rules is not None and state_12_to_simple is not None:
state_12 = state_from_rules(self.mu, self.lam)
return state_12_to_simple(state_12)
# Fallback: limiares fixos
if self.mu >= THRESHOLD_TRUE and self.lam <= (1 - THRESHOLD_TRUE):
return "Verdadeiro"
if self.mu <= THRESHOLD_FALSE and self.lam >= (1 - THRESHOLD_FALSE):
return "Falso"
if self.mu >= THRESHOLD_INCONSISTENT and self.lam >= THRESHOLD_INCONSISTENT:
return "Inconsistente_local" # explosão GENTIL — não trivializa
if self.mu <= THRESHOLD_INDETERMINATE and self.lam <= THRESHOLD_INDETERMINATE:
return "Indeterminado"
return "Intermediário" # ex: morno entre quente e frio
@property
def state_12(self) -> Optional[str]:
"""Estado lógico de 12 valores (reticulado) conforme Fuzzy.txt, se regras carregadas."""
if state_from_rules is not None:
return state_from_rules(self.mu, self.lam)
return None
@property
def truth_value(self) -> float:
"""Valor-verdade escalar normalizado para saída final."""
return round((self.mu + (1 - self.lam)) / 2.0, 4)
def __str__(self) -> str:
return (
f" μ={self.mu:.3f} λ={self.lam:.3f} "
f"Gc={self.certainty:+.3f} Gct={self.contradiction:+.3f} "
f"v={self.truth_value:.3f} [{self.state}]\n"
f" \"{self.proposition}\""
)
# ─────────────────────────────────────────────────────────────────────────────
# Motor paraconsistente
# ─────────────────────────────────────────────────────────────────────────────
class ParaconsistentEngine:
"""
Avalia as hipóteses kantiana (L2) e atribui valores-verdade
paraconsistentes a cada uma.
Pode operar em dois modos:
- Modo heurístico (padrão): usa apenas o banco de conhecimento.
- Modo neural: se um TruthScoringModel for fornecido, usa o modelo
para calcular (μ, λ) compatíveis com a Lógica Anotada.
"""
def __init__(
self,
neural_model: Optional["TruthScoringModel"] = None,
neural_tokenizer=None,
device: Optional[torch.device] = None,
) -> None:
self.neural_model = neural_model
self.neural_tokenizer = neural_tokenizer
self.device = device or torch.device("cuda" if torch.cuda.is_available() else "cpu")
def evaluate(
self,
propositions: List[Tuple[str, float]], # (texto, peso_de_prioridade_L2)
knowledge_base: Dict[str, float], # termo → grau de evidência no BD
) -> List[ParaconsistentValue]:
"""
Para cada proposição:
μ = evidência favorável extraída do banco de dados
λ = evidência contrária = 1 − f(compatibilidade)
"""
results: List[ParaconsistentValue] = []
for prop_text, l2_priority in propositions:
mu, lam = self._compute_annotations(prop_text, l2_priority, knowledge_base)
pv = ParaconsistentValue(proposition=prop_text, mu=mu, lam=lam)
results.append(pv)
# Ordena por valor-verdade descendente
results.sort(key=lambda pv: pv.truth_value, reverse=True)
return results
# ------------------------------------------------------------------ #
# Anotação μ / λ #
# ------------------------------------------------------------------ #
def _compute_annotations(
self,
text: str,
l2_priority: float,
kb: Dict[str, float],
) -> Tuple[float, float]:
"""
Calcula (μ, λ) para uma proposição.
Se um modelo neural estiver disponível, usa-o para obter anotações
compatíveis com L3. Caso contrário, volta para a heurística original
baseada apenas no banco de conhecimento e em contradições locais.
"""
if self.neural_model is not None and self.neural_tokenizer is not None and neural_annotations is not None:
mu, lam, _, _ = neural_annotations(self.neural_model.to(self.device), self.neural_tokenizer, text)
# Pequena modulação pela prioridade de L2 para manter a integração
mu = min(1.0, mu * (0.5 + 0.5 * l2_priority))
lam = max(0.0, lam * (1.0 - 0.3 * l2_priority))
return round(mu, 4), round(lam, 4)
import re
tokens = set(re.findall(r"[a-záàãâéêíóôõúüç]+", text.lower()))
kb_scores = [kb.get(t, 0.0) for t in tokens if kb.get(t, 0.0) > 0]
mu_kb = sum(kb_scores) / len(kb_scores) if kb_scores else 0.3
contradiction_detected = self._has_antonym_pair(tokens, kb)
lam_base = 0.8 if contradiction_detected else (1.0 - mu_kb)
mu = min(1.0, mu_kb * (0.5 + 0.5 * l2_priority))
lam = max(0.0, lam_base * (1.0 - 0.3 * l2_priority))
return round(mu, 4), round(lam, 4)
ANTONYM_PAIRS = [
("quente", "frio"), ("quente", "gelado"),
("verdadeiro", "falso"), ("real", "fictício"),
("afirmativo", "negativo"), ("possível", "impossível"),
]
def _has_antonym_pair(self, tokens: set, kb: Dict[str, float]) -> bool:
for a, b in self.ANTONYM_PAIRS:
if a in tokens and b in tokens:
return True
return False
# ------------------------------------------------------------------ #
# Consistência global: verifica se sistema trivializou #
# ------------------------------------------------------------------ #
@staticmethod
def check_global_consistency(values: List[ParaconsistentValue]) -> bool:
"""
Retorna True se o sistema é globalmente consistente
(nenhuma trivialização — todos os estados válidos).
Uma trivialização ocorre se TODAS as proposições são
'Inconsistente_local' sem nenhum 'Verdadeiro' ou 'Intermediário'.
"""
states = {pv.state for pv in values}
if states == {"Inconsistente_local"}:
return False # trivialização global
return True # explosão gentil — sistema consistente
|