aboutsummaryrefslogtreecommitdiff
path: root/docs/architecture.md
blob: 333fa9940caa301db9374e940b5a34ff2d9c44c9 (plain)
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
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
# ATP Engine — Architecture Design Document

> **Version**: 1.0  
> **Target**: Extensible first-order logic theorem prover (saturation-based)  
> **Language**: C++20  
> **Build**: CMake 3.20+, GoogleTest, ANTLR4 (C++ target)

---

## Table of Contents

1. [Design Philosophy](#1-design-philosophy)
2. [System Architecture](#2-system-architecture)
3. [Directory Structure](#3-directory-structure)
4. [Module Specifications](#4-module-specifications)
5. [Data Flow](#5-data-flow)
6. [Known Limitations & Future Work](#6-known-limitations--future-work)
7. [References](#7-references)

---

## 1. Design Philosophy

### 1.1 Core Principles

| Principle | Rationale |
|-----------|-----------|
| **Bottom-up construction** | Build from the memory layer upward. A stable Term Bank is the foundation everything else rests on. |
| **Strict layered dependencies** | Dependencies flow in one direction only: `core → infer → index → simplify → search → proof`. No cycles. |
| **Interface-implementation separation** | Headers in `include/atp/` define interfaces; `src/` contains implementations. Modules communicate through stable APIs. |
| **Premature abstraction avoidance** | V1 hardcodes resolution + factoring. Abstract strategy selection (`strategy/`) is scaffolded but not wired in until V2. |
| **Parse once, reason forever** | Text parsing (ANTLR4) is a one-shot frontend concern. The core engine operates entirely on integer IDs, never on strings. |

### 1.2 Design Influences

This architecture draws from three production-grade theorem provers:

- **E Prover** (Stephan Schulz) — Given Clause Loop, term sharing via hash consing, age-weight clause selection.
- **Vampire** (Andrei Voronkov et al.) — AVATAR splitting, superposition calculus, discrimination tree indexing.
- **Otter** (William McCune) — Original set-of-support strategy, subsumption, demodulation.

### 1.3 Versioning Strategy

```
V1  →  Pure first-order resolution (no equality). Solve the Monkey-Banana problem.
V2  →  Pluggable inference rules (Calculus), age-weight scheduling, variable standardization.
V3  →  Equality reasoning (paramodulation/superposition), term orderings (KBO/LPO).
V4  →  AVATAR splitting, SAT solver integration, TPTP competition readiness.
```

---

## 2. System Architecture

### 2.1 Layered Architecture Overview

```
┌──────────────────────────────────────────────────────────────────────┐
│                         Frontend Layer                               │
│  TPTP File → ANTLR4 Lexer/Parser → AST Builder → tptp_parser.h     │
└────────────────────────────────┬─────────────────────────────────────┘
                                 │ Formula (internal AST)
┌────────────────────────────────▼─────────────────────────────────────┐
│                       Normalization Layer                             │
│  formula.h → clausifier.h (NNF → Skolem → CNF)                     │
└────────────────────────────────┬─────────────────────────────────────┘
                                 │ Clause set
┌────────────────────────────────▼─────────────────────────────────────┐
│                        Search Layer (Control)                        │
│                                                                      │
│   ┌──────────────┐  select   ┌─────────────────┐  resolve with all  │
│   │ Unprocessed  │─────────→ │  Given Clause   │─────────────────→  │
│   │ (pri queue)  │           │  (selected)     │                    │
│   └──────▲───────┘           └─────────────────┘                    │
│          │ enqueue survivors                                         │
│   ┌──────┴───────┐           ┌─────────────────┐                    │
│   │  Simplify &  │←──────────│  New Clauses    │                    │
│   │  Filter      │           │  (resolvents)   │                    │
│   └──────────────┘           └─────────────────┘                    │
│          │ if empty clause found                                     │
└──────────┼───────────────────────────────────────────────────────────┘
           ▼
┌──────────────────────────────────────────────────────────────────────┐
│                         Output Layer                                 │
│  proof_trace.h → DFS backtrack → format & print action sequence     │
└──────────────────────────────────────────────────────────────────────┘

═══════════════════════ Shared Foundation ══════════════════════════════
┌──────────────────────────────────────────────────────────────────────┐
│                    Core Layer (Data Plane)                            │
│  Symbol Table │ Term Bank (hash consing) │ Clause Store │ Types     │
└──────────────────────────────────────────────────────────────────────┘
```

### 2.2 Dependency Graph

```
frontend ──→ normalize ──→ core
                             ↑
search ──→ simplify ──→ index ──→ infer ──→ core
                                             ↑
                                         rewrite ─┘
  proof ──→ core/clause_store

  strategy/ ──→ (V2: wired into search)
```

All arrows point downward/inward. `proof` depends only on `clause_store`, not on `search` (no reverse dependency).

---

## 3. Directory Structure

```
richard-atp-engine/
├── CMakeLists.txt                # Top-level build (C++20, clang-tidy, clang-format, sanitizers)
├── .clang-format                 # Google-based, 4-space indent, 100-col
├── .clang-tidy                   # Strict checks, filtered for generated code
├── .gitignore
│
├── grammar/
│   └── TPTP.g4                   # ANTLR4 grammar for TPTP fof/cnf syntax
│
├── docs/
│   └── architecture.md           # This document
│
├── data/
│   └── monkey_banana.p           # First integration test (TPTP format)
│
├── include/atp/
│   ├── core/                     # Data plane — memory foundation
│   │   ├── types.h               #   TermId, ClauseId, SymbolId, SortId, SymbolKind, InferenceRule
│   │   ├── symbol_table.h        #   String interning with kind/sort metadata
│   │   ├── term.h                #   Flat term struct (symbol_id + args)
│   │   ├── literal.h             #   Polarity + atom TermId
│   │   ├── clause.h              #   Literal vector + provenance (parents, rule, depth)
│   │   ├── clause_store.h        #   Central clause ownership, ClauseId allocation
│   │   └── term_bank.h           #   Hash-consing pool for structural sharing
│   │
│   ├── normalize/                # Normalization — FOL formula to CNF
│   │   ├── formula.h             #   FOL AST nodes (And, Or, Not, Forall, Exists, ...)
│   │   └── clausifier.h          #   Eliminate →/↔ → NNF → Skolemize → distribute → flatten
│   │
│   ├── infer/                    # Compute plane — stateless inference operators
│   │   ├── substitution.h        #   Variable binding map (stack-lifetime)
│   │   ├── unification.h         #   MGU with optional occurs check
│   │   └── resolution.h          #   Binary resolution + factoring
│   │
│   ├── index/                    # Index plane — fast term retrieval
│   │   └── discrim_tree.h        #   Discrimination tree (unifiable / instance / generalization queries)
│   │
│   ├── simplify/                 # Redundancy elimination
│   │   ├── tautology.h           #   Complementary literal detection
│   │   └── subsumption.h         #   Forward/backward subsumption (backed by discrim tree)
│   │
│   ├── search/                   # Control plane — proof search scheduling
│   │   ├── clause_queue.h        #   Comparators (BFS, weight-based)
│   │   └── prover.h              #   Given Clause Loop (V1: hardcoded rules; V2: Calculus-driven)
│   │
│   ├── proof/                    # Output — proof reconstruction
│   │   └── proof_trace.h         #   DFS backtrack from empty clause, formatting
│   │
│   ├── rewrite/                  # Equality reasoning (V3 extension point)
│   │   ├── ordering.h            #   Abstract TermOrdering (KBO, LPO)
│   │   └── paramodulation.h      #   Paramodulation + demodulation
│   │
│   ├── strategy/                 # Dynamic algorithm selection (V2 extension point)
│   │   ├── inference_rule.h      #   GeneratingRule / SimplifyingRule interfaces
│   │   ├── calculus.h            #   Bundles rules + heuristics + ordering
│   │   └── problem_analyzer.h   #   Feature detection + calculus selection
│   │
│   └── utils/
│       ├── hash_utils.h          #   Hash combination (boost::hash_combine equivalent)
│       └── logger.h              #   Compile-time-filtered logging macros
│
├── src/                          # Implementation files (mirror include/ structure)
│   ├── core/
│   ├── normalize/
│   ├── infer/
│   ├── index/
│   ├── simplify/
│   ├── search/
│   ├── proof/
│   ├── rewrite/
│   ├── strategy/
│   ├── frontend/
│   └── main.cpp
│
└── tests/                        # GoogleTest unit tests
    ├── CMakeLists.txt
    ├── test_term_bank.cc
    ├── test_unification.cc
    ├── test_clausifier.cc
    ├── test_resolution.cc
    ├── test_discrim_tree.cc
    └── test_prover.cc           # End-to-end: Monkey-Banana problem
```

---

## 4. Module Specifications

### 4.1 Core Layer (`core/`)

The core layer owns all persistent data. Every other module receives `const&` or `&` references to core objects — never raw pointers, never ownership.

#### `types.h` — Type System Foundation

```cpp
using SymbolId = uint32_t;   // Index into SymbolTable
using TermId   = uint32_t;   // Index into TermBank (hash-consed)
using ClauseId = uint32_t;   // Index into ClauseStore
using SortId   = uint16_t;   // For sorted logic (V3+), default = kUnsorted

enum class SymbolKind : uint8_t {
    kVariable, kConstant, kFunction, kPredicate, kSkolem
};

enum class InferenceRule : uint16_t {
    kInput = 0, kResolution = 1, kFactoring = 2,       // Core
    kParamodulation = 100, kDemodulation = 101, ...     // Equality (V3)
    kSubsumption = 200, kTautologyElim = 201, ...       // Simplification
    kTheoryResolution = 300, ...                         // Theory (V4)
};
```

**Design decisions**:
- `uint32_t` IDs instead of pointers — enables flat array storage, trivial comparison, and cache-friendly iteration.
- `InferenceRule` uses `uint16_t` with reserved ranges (0-99 core, 100-199 equality, 200-299 simplification, 300+ theory) to avoid enum churn as the system grows.
- `SymbolKind` enables distinguishing variables from function symbols at the symbol level, critical for unification and sorted logic.
- `SortId` is present but unused in V1 (`kUnsorted`), ready for sorted unification in V3.

#### `symbol_table.h` — String Interning

```cpp
struct SymbolInfo {
    std::string name;
    SymbolKind kind;
    uint16_t arity;
    SortId sort;
};

class SymbolTable {
    SymbolId intern(string_view name, SymbolKind kind, uint16_t arity, SortId sort);
    string_view getName(SymbolId id) const;
    const SymbolInfo& getInfo(SymbolId id) const;
    bool isVariable(SymbolId id) const;
};
```

Each unique symbol string is stored exactly once. All downstream modules work with `SymbolId` integers. The `SymbolInfo` metadata (kind, arity, sort) enables type-checking during unification without string inspection.

#### `term_bank.h` — Hash Consing

```cpp
class TermBank {
    TermId makeTerm(SymbolId symbol, span<const TermId> args);
    TermId makeVar(SymbolId var_symbol);
    const Term& getTerm(TermId id) const;
    bool isVariable(TermId id) const;
};
```

**Hash consing guarantee**: If `makeTerm(f, {a, b})` is called twice with the same `f`, `a`, `b`, it returns the same `TermId`. This makes structural equality a single integer comparison (`O(1)`), which is the foundation for efficient indexing and subsumption.

**Memory layout**: Terms are stored in a flat arena. Each `Term` header (`symbol_id` + `arity`) is followed by its argument `TermId` array in contiguous memory. No heap allocation per term.

#### `clause_store.h` — Clause Ownership

```cpp
class ClauseStore {
    ClauseId addClause(Clause clause);
    const Clause& getClause(ClauseId id) const;
    Clause& getMutableClause(ClauseId id);
    void forEach(const function<void(const Clause&)>& visitor) const;
};
```

Separated from `Prover` so that proof extraction, simplification, and search all access clauses through the same stable interface. Changing the search strategy (OTTER loop → DISCOUNT loop) does not affect clause storage.

---

### 4.2 Normalization Layer (`normalize/`)

#### `formula.h` — First-Order Logic AST

```cpp
enum class FormulaKind : uint8_t {
    kAtom, kNot, kAnd, kOr, kImplies, kIff, kForall, kExists
};

struct Formula {
    FormulaKind kind;
    TermId atom;                              // for kAtom
    string var_name;                          // for quantifiers
    vector<unique_ptr<Formula>> children;     // sub-formulas
};
```

This is the **pre-clausification** representation. The parser builds `Formula` trees; the clausifier destroys them and produces `Clause` vectors. Formula nodes are short-lived — they exist only during the normalization phase.

#### `clausifier.h` — CNF Conversion Pipeline

```
Input Formula
    │
    ▼ eliminateImplication()     — P → Q  becomes  ¬P ∨ Q
    ▼ eliminateBiconditional()   — P ↔ Q  becomes  (¬P ∨ Q) ∧ (P ∨ ¬Q)
    ▼ pushNegation()             — ¬(P ∧ Q) becomes ¬P ∨ ¬Q       (NNF)
    ▼ skolemize()                — ∀x∃y.P(x,y) becomes ∀x.P(x, sk(x))
    ▼ dropUniversal()            — All remaining vars are implicitly ∀
    ▼ distribute()               — (P ∨ (Q ∧ R)) becomes (P ∨ Q) ∧ (P ∨ R)
    ▼ flatten()                  — Nested ∧ at top level → separate clauses
    │
    ▼
Output: vector<Clause>
```

**Critical**: `skolemize()` must correctly handle nested quantifier scopes. Given `∀x ∃y ∀z ∃w. P(x,y,z,w)`, the Skolem functions are `y = sk1(x)` and `w = sk2(x, z)` — each existential variable becomes a function of all universally quantified variables in its scope.

---

### 4.3 Inference Layer (`infer/`)

#### `substitution.h` — Variable Bindings

A substitution `σ = {X → f(a), Y → Z}` maps variables to terms. Key properties:

- **Stack lifetime**: Created during unification, applied to build the resolvent, then discarded. Never stored long-term.
- **Chain resolution**: `resolve(X)` walks the binding chain (`X → Y → f(a)`) to the final value.
- **Idempotence**: After applying a substitution, no variables in the result have bindings in the same substitution.

#### `unification.h` — Most General Unifier

```cpp
bool unify(const TermBank& bank, TermId t1, TermId t2, Substitution& subst,
           const UnificationConfig& config);
```

The Robinson unification algorithm, operating on `TermId` values via the `TermBank`. Returns the Most General Unifier (MGU) if one exists.

**Occurs check**: Configurable via `UnificationConfig::enable_occurs_check`. Disabled for situation calculus planning (performance); enabled for general mathematical reasoning (soundness — prevents infinite terms like `X = f(X)`).

#### `resolution.h` — Inference Rules

```cpp
optional<Clause> resolve(bank, clause1, lit_idx1, clause2, lit_idx2);
vector<Clause>   allResolvents(bank, clause1, clause2);
vector<Clause>   factor(bank, clause);
```

**Binary resolution**: Given clauses `{P(x), Q(x)}` and `{¬P(a), R(a)}`, if `P(x)` and `P(a)` unify under `σ = {x→a}`, produce resolvent `{Q(a), R(a)}`.

**Factoring**: Given clause `{P(x), P(a), Q(x)}`, if `P(x)` and `P(a)` unify, produce `{P(a), Q(a)}`. Factoring is necessary for completeness of binary resolution.

---

### 4.4 Index Layer (`index/`)

#### `discrim_tree.h` — Discrimination Tree

A trie-based index over the flattened structure of terms. Supports three query types:

| Query | Use Case | Direction |
|-------|----------|-----------|
| `queryUnifiable(t)` | Find resolution partners | Bidirectional matching |
| `queryInstances(t)` | Backward subsumption | Pattern more general than indexed |
| `queryGeneralizations(t)` | Forward subsumption | Indexed more general than query |

The tree is built by linearizing terms in pre-order: `f(g(a), b)` becomes the key sequence `[f, g, a, *, b]` where `*` represents a variable (wildcard). Lookups traverse the trie, branching at decision points.

---

### 4.5 Simplification Layer (`simplify/`)

#### `tautology.h`

Detects clauses containing both `P` and `¬P` for the same atom. These are logically true and can be safely discarded without affecting completeness.

#### `subsumption.h`

Backed by the discrimination tree. Two operations:

- **Forward subsumption**: Before enqueuing a new clause `C`, check if any existing clause `D` subsumes it (`D ⊆ C` — every literal of `D` matches a literal of `C`). If yes, discard `C`.
- **Backward subsumption**: After adding `C`, check if `C` subsumes any existing clause `D`. If yes, remove `D`.

Subsumption is the single most important simplification rule for keeping the clause set manageable.

---

### 4.6 Search Layer (`search/`)

#### `prover.h` — The Given Clause Loop

The main proof search loop, following the OTTER/E paradigm:

```
procedure GivenClauseLoop(initial_clauses):
    Unprocessed ← priority_queue(initial_clauses)
    Processed   ← empty_set

    while Unprocessed is not empty:
        given ← Unprocessed.pop()          // clause selection

        if given is tautology: continue
        if given is forward-subsumed: continue

        // Backward simplification
        remove from Processed all clauses subsumed by given

        // Inference
        for each clause C in Processed:
            for each resolvent R of (given, C):
                if R is empty clause: RETURN PROOF FOUND
                if R is not tautology and not forward-subsumed:
                    Unprocessed.push(R)

        // Generate factors of given
        for each factor F of given:
            if F is empty clause: RETURN PROOF FOUND
            Unprocessed.push(F)

        Processed.add(given)

    RETURN SATURATED (no proof exists)
```

**V1 configuration**:
```cpp
struct ProverConfig {
    ClauseComparator comparator = bfsCompare;  // BFS for shortest proof
    size_t max_clauses = 100000;
    size_t max_iterations = 500000;
    bool enable_occurs_check = false;
};
```

---

### 4.7 Proof Layer (`proof/`)

#### `proof_trace.h`

When the empty clause `□` is derived, the prover halts. Proof extraction walks the `parent1`/`parent2` pointers stored in each `Clause` via DFS, reconstructing the derivation tree from `□` back to input axioms.

The output is a sequence of `ProofStep` records, each containing the inference rule used and the parent clause IDs. This can be formatted as a human-readable proof or (future) TSTP-compliant output.

---

### 4.8 Extension Point: Rewriting (`rewrite/`)

Scaffolded for V3. Not compiled in V1.

#### `ordering.h` — Abstract Term Ordering

```cpp
class TermOrdering {
    virtual OrderResult compare(const TermBank& bank, TermId t1, TermId t2) const = 0;
};
```

Concrete implementations (future): Knuth-Bendix Ordering (KBO), Lexicographic Path Ordering (LPO). These are required for ordered resolution and superposition calculus — they ensure that rewriting always terminates.

#### `paramodulation.h` — Equality Reasoning

Paramodulation replaces a subterm in one clause with an equal term from another. Combined with a term ordering, this becomes the **superposition calculus** — the dominant approach for first-order logic with equality.

---

### 4.9 Extension Point: Strategy (`strategy/`)

Scaffolded for V2. Not compiled in V1.

The strategy module will enable dynamic algorithm selection based on problem analysis:

```
Input clauses → ProblemAnalyzer → ProblemFeatures → selectCalculus() → Calculus
                                                                         │
                        ┌────────────────────────────────────────────────┘
                        ▼
             Calculus = { generating_rules[], simplifying_rules[],
                          comparator, ordering, resource_limits }
                        │
                        ▼
                   Prover.prove(calculus)
```

This prevents search space explosion by loading only the inference rules relevant to the problem. A pure propositional problem would never load the paramodulation rule; an equality-heavy problem would skip pure resolution in favor of superposition.

---

## 5. Data Flow

### 5.1 End-to-End Pipeline

```
monkey_banana.p (TPTP text file)
       │
       ▼
  [tptp_parser]  ANTLR4 lexer/parser → parse tree → Formula AST
       │
       ▼
  [clausifier]   NNF → Skolemize → CNF → vector<Clause>
       │
       ▼
  [prover]       Given Clause Loop (resolution + factoring + subsumption)
       │
       ├── Normal: derive new clauses, enqueue, repeat
       │
       └── Empty clause found!
              │
              ▼
        [proof_trace]  DFS backtrack through parent pointers
              │
              ▼
        Human-readable proof output
```

### 5.2 Term Lifecycle

```
String "do(walk, S0)"
       │
       ▼ SymbolTable::intern()
  SymbolIds: { do=0, walk=1, S0=2 }
       │
       ▼ TermBank::makeTerm()
  TermIds:   S0_term=0, walk_term=1, do_walk_S0=2
       │     (hash-consed: calling makeTerm(do, [walk, S0]) again returns 2)
       │
       ▼ Used everywhere as integer IDs
  Clause { literals: [ Literal{atom=2, positive=true} ] }
```

### 5.3 Substitution Lifecycle

```
Unify( P(X, f(Y)),  P(a, f(b)) )
       │
       ▼ unify() creates stack-local Substitution
  σ = { X → a,  Y → b }
       │
       ▼ Apply σ to build resolvent clause
  New terms created in TermBank (hash-consed)
       │
       ▼ Substitution goes out of scope — destroyed
  Only the new TermIds persist
```

---

## 6. Known Limitations & Future Work

### 6.1 Critical Gaps (Ordered by Priority)

#### Gap 1: No Variable Standardization Apart

**Impact**: Correctness bug if not addressed before first real resolution.

When resolving clause `{P(X)}` with clause `{¬P(X), Q(X)}`, variable `X` in the two clauses must be treated as distinct. Without renaming one clause's variables (e.g., `X → X'`), the unifier incorrectly constrains both clauses' variables.

**Fix**: Add `renameVariables(Clause&, TermBank&, uint32_t& next_var_id)` to `infer/` before Phase 2 resolution implementation.

#### Gap 2: No Literal Selection Function

**Impact**: Search space is ~10x larger than necessary.

Ordered resolution restricts inference to the **maximal literal** (under a term ordering) in each clause. Our `Prover` currently has no concept of selecting which literal to resolve on — it tries all pairs.

**Fix**: V2 — add `LiteralSelector` interface, parameterized by `TermOrdering`.

#### Gap 3: Single Clause Queue (No Age-Weight Interleaving)

**Impact**: Incomplete or extremely slow on hard problems.

A single `ClauseComparator` cannot implement the age-weight ratio that E Prover uses: "pick the lightest clause 5 times, then the oldest clause once." This interleaving is crucial for both performance and completeness.

**Fix**: V2 — replace `ClauseComparator` with a `ClauseQueue` abstract class supporting multi-queue round-robin.

#### Gap 4: Single Index Type

**Impact**: Suboptimal retrieval performance.

The discrimination tree handles forward matching well but is not ideal for all query patterns. Modern ATPs maintain multiple complementary indices:

| Index Type | Best For |
|-----------|----------|
| Discrimination Tree | Forward matching (generalization queries) |
| Substitution Tree | Backward matching + unification candidates |
| Fingerprint Index | O(1) pre-filtering of non-unifiable pairs |
| Feature Vector Index | Clause-level subsumption checks |

**Fix**: V3 — abstract `TermIndex` interface, multiple implementations.

#### Gap 5: No Preprocessing Pipeline

**Impact**: Missed optimization opportunities.

Between clausification and the main loop, modern ATPs perform definition unfolding, pure literal elimination, equality axiom injection, and miniscoping. Our `normalize/` layer has only the clausifier.

**Fix**: V2 — extend `normalize/` into a transform pipeline with pluggable passes.

#### Gap 6: No AVATAR Splitting

**Impact**: Cannot compete on structured problems.

AVATAR splits clauses with independent variable sets into propositional components managed by an embedded SAT solver. This is the single largest performance innovation in ATP in the last decade (10-100x speedup on structured problems).

**Fix**: V4 — add `sat/` module with MiniSat/CaDiCaL integration + splitting logic in `search/`.

### 6.2 Non-Critical Gaps

| Gap | Category | Target Version |
|-----|----------|----------------|
| TSTP proof output format | Interoperability | V2 |
| Watched literals for unit propagation | Performance | V3 |
| Clause arena (replace `vector<Literal>`) | Memory | V3 |
| ML-guided clause selection | Research | V5+ |
| Parallel portfolio mode | Performance | V5+ |

---

## 7. References

1. Schulz, S. (2013). *System Description: E 1.8*. CADE-24.
2. Kovács, L., Voronkov, A. (2013). *First-Order Theorem Proving and Vampire*. CAV 2013.
3. Voronkov, A. (2014). *AVATAR: The Architecture for First-Order Theorem Provers*. CAV 2014.
4. Nieuwenhuis, R., Rubio, A. (2001). *Paramodulation-Based Theorem Proving*. Handbook of Automated Reasoning.
5. Sutcliffe, G. (2009). *The TPTP Problem Library and Associated Infrastructure*. Journal of Automated Reasoning.
6. Robinson, J.A. (1965). *A Machine-Oriented Logic Based on the Resolution Principle*. JACM.