diff options
Diffstat (limited to 'docs/architecture.md')
| -rw-r--r-- | docs/architecture.md | 630 |
1 files changed, 630 insertions, 0 deletions
diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..333fa99 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,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. |
