File size: 11,611 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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
"""
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