File size: 5,371 Bytes
ab7abc7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
nanogentzen/kernel.py
100% Deterministic Gentzen Sequent Calculus Engine for Intuitionistic Logic (LI).
"""
from dataclasses import dataclass
from typing import List, Optional, Tuple

class Formula:
    def to_str(self) -> str:
        raise NotImplementedError
    def __repr__(self) -> str:
        return self.to_str()
    def __eq__(self, other) -> bool:
        return isinstance(other, Formula) and self.to_str() == other.to_str()
    def __hash__(self) -> int:
        return hash(self.to_str())

@dataclass(frozen=True)
class Var(Formula):
    name: str
    def to_str(self) -> str:
        return self.name

@dataclass(frozen=True)
class Not(Formula):
    inner: Formula
    def to_str(self) -> str:
        return f"~{self.inner.to_str()}"

@dataclass(frozen=True)
class And(Formula):
    left: Formula
    right: Formula
    def to_str(self) -> str:
        return f"({self.left.to_str()} & {self.right.to_str()})"

@dataclass(frozen=True)
class Or(Formula):
    left: Formula
    right: Formula
    def to_str(self) -> str:
        return f"({self.left.to_str()} | {self.right.to_str()})"

@dataclass(frozen=True)
class Imp(Formula):
    left: Formula
    right: Formula
    def to_str(self) -> str:
        return f"({self.left.to_str()} => {self.right.to_str()})"

@dataclass(frozen=True)
class Sequent:
    gamma: Tuple[Formula, ...]  # Antecedents (Gamma)
    delta: Tuple[Formula, ...]  # Succedents (Delta, |Delta| <= 1 for LI)

    def is_axiom(self) -> bool:
        """Identity Axiom: Gamma, A |- A and Ex Falso: 0, Gamma |- Delta."""
        delta_set = set(self.delta)
        if any(f in delta_set for f in self.gamma):
            return True
        if any(isinstance(f, Var) and f.name in ("0", "FALSUM", "false") for f in self.gamma):
            return True
        return False

    def to_str(self) -> str:
        g = ", ".join(f.to_str() for f in self.gamma) if self.gamma else "0"
        d = ", ".join(f.to_str() for f in self.delta) if self.delta else "0"
        return f"{g} |- {d}"

    def __repr__(self) -> str:
        return self.to_str()

RULES: List[str] = [
    "AXIOM",
    "R_IMP",
    "L_IMP",
    "R_AND",
    "L_AND",
    "R_OR_1",
    "R_OR_2",
    "L_OR",
    "R_NOT",
    "L_NOT",
    "L_CONTR",
]

def apply_rule(seq: Sequent, rule: str, idx: int = 0) -> Optional[List[Sequent]]:
    """Applies inverse Gentzen LI rules backwards to reduce sequents into premises."""
    gamma, delta = list(seq.gamma), list(seq.delta)
    if rule == "AXIOM":
        return [] if seq.is_axiom() else None

    # (|- =>) : Gamma |- (A => B) decomposes to A, Gamma |- B
    if rule == "R_IMP" and delta and isinstance(delta[0], Imp):
        return [Sequent(tuple([delta[0].left] + gamma), (delta[0].right,))]

    # (=> |-) : (A => B), Gamma |- Delta decomposes to Gamma |- A and B, Gamma |- Delta
    if rule == "L_IMP" and idx < len(gamma) and isinstance(gamma[idx], Imp):
        f = gamma.pop(idx)
        return [
            Sequent(tuple(gamma), (f.left,)),
            Sequent(tuple([f.right] + gamma), tuple(delta)),
        ]

    # (|- &) : Gamma |- (A & B) decomposes to Gamma |- A and Gamma |- B
    if rule == "R_AND" and delta and isinstance(delta[0], And):
        return [
            Sequent(tuple(gamma), (delta[0].left,)),
            Sequent(tuple(gamma), (delta[0].right,)),
        ]

    # (& |-) : (A & B), Gamma |- Delta decomposes to A, B, Gamma |- Delta
    if rule == "L_AND" and idx < len(gamma) and isinstance(gamma[idx], And):
        f = gamma.pop(idx)
        return [Sequent(tuple([f.left, f.right] + gamma), tuple(delta))]

    # (|- v)1 : Gamma |- (A v B) decomposes to Gamma |- A
    if rule == "R_OR_1" and delta and isinstance(delta[0], Or):
        return [Sequent(tuple(gamma), (delta[0].left,))]

    # (|- v)2 : Gamma |- (A v B) decomposes to Gamma |- B
    if rule == "R_OR_2" and delta and isinstance(delta[0], Or):
        return [Sequent(tuple(gamma), (delta[0].right,))]

    # (v |-) : (A v B), Gamma |- Delta decomposes to A, Gamma |- Delta and B, Gamma |- Delta
    if rule == "L_OR" and idx < len(gamma) and isinstance(gamma[idx], Or):
        f = gamma.pop(idx)
        return [
            Sequent(tuple([f.left] + gamma), tuple(delta)),
            Sequent(tuple([f.right] + gamma), tuple(delta)),
        ]

    # (|- ~) : Gamma |- ~A decomposes to A, Gamma |- 0
    if rule == "R_NOT" and delta and isinstance(delta[0], Not):
        return [Sequent(tuple([delta[0].inner] + gamma), ())]

    # (~ |-) : ~A, Gamma |- decomposes to Gamma |- A
    if rule == "L_NOT" and idx < len(gamma) and isinstance(gamma[idx], Not):
        f = gamma.pop(idx)
        return [Sequent(tuple(gamma), (f.inner,))]

    # Structural Contraction (contr |-)
    if rule == "L_CONTR" and idx < len(gamma):
        f = gamma[idx]
        return [
            Sequent(
                tuple([f, f] + [x for i, x in enumerate(gamma) if i != idx]),
                tuple(delta),
            )
        ]
    return None

def verify_proof_tree(node: dict) -> bool:
    """Recursively validates that a generated proof tree is 100% mathematically sound."""
    rule_str = node.get("rule", "")
    branches = node.get("branches", [])

    if rule_str == "AXIOM":
        return len(branches) == 0
    if not branches:
        return False
    return all(verify_proof_tree(child) for child in branches)