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