|
1 | | -# Practical Halting Problem Analyzer |
| 1 | +# A Practical Halting Analyzer |
2 | 2 |
|
3 | | -This project is a practical, multi-layered analyzer designed to determine whether a given Python script will halt or run indefinitely. It serves as an exploration of computability theory, combining static analysis, symbolic execution, and dynamic tracing to provide a definitive verdict for a wide class of programs. |
| 3 | +[](https://opensource.org/licenses/MIT) |
4 | 4 |
|
5 | | -While the Halting Problem is theoretically unsolvable for all possible programs, this tool provides a robust heuristic solution that correctly handles a variety of complex cases, including deep recursion, subtle infinite loops, and self-referential code. |
| 5 | +A multi-layered heuristic engine designed to practically analyze the halting properties of Python scripts, navigating the complexities of the undecidable Halting Problem. |
6 | 6 |
|
7 | | ---- |
| 7 | +## The Problem: The Halting Problem |
8 | 8 |
|
9 | | -## How It Works: A Four-Phase Approach |
| 9 | +In 1936, Alan Turing proved that it is impossible to create a universal algorithm that can determine, for all possible programs, whether they will finish running (halt) or continue to run forever. No perfect, general-purpose solution can ever exist. |
10 | 10 |
|
11 | | -The analyzer subjects a script to a cascading series of increasingly powerful analyses. It stops and returns a result as soon as any single phase can make a definitive determination. |
| 11 | +This project does not attempt to "solve" the Halting Problem. Instead, it provides a practical, multi-phase heuristic approach to analyze Python code, successfully identifying halting and non-halting behavior in a wide range of real-world and adversarial scenarios. |
12 | 12 |
|
13 | | -1. **Phase 1: Static Preparation** |
14 | | - The analyzer first inspects the program's source code without running it (using its Abstract Syntax Tree). It looks for "low-hanging fruit"—obvious signs of halting (e.g., no loops) or non-halting (e.g., a `while True:` loop). This provides a fast-path for simple cases. |
| 13 | +## The Solution: A Multi-Layered Heuristic Defense |
15 | 14 |
|
16 | | -2. **Phase 2: Symbolic Analysis** |
17 | | - For programs with more complex loops, the analyzer uses the Z3 theorem prover to formally prove termination. It models the loop's variables and conditions mathematically and attempts to synthesize a "ranking function"—a formal proof that the loop's state is converging towards a termination condition. |
| 15 | +This analyzer employs a "defense-in-depth" strategy. It subjects a given program to a series of increasingly sophisticated and computationally expensive analysis phases. If any phase can make a definitive decision, the analysis stops, ensuring maximum efficiency. |
18 | 16 |
|
19 | | -3. **Phase 3: Dynamic Tracing** |
20 | | - If the code's behavior cannot be determined statically, the analyzer runs the program in a sandboxed environment and observes its execution. It uses a sophisticated cycle detection algorithm ("Floyd's Tortoise and Hare") to find repeating patterns in the execution trace, which are a strong indicator of an infinite loop. It also has safeguards against runaway recursion. |
| 17 | +### Core Architecture: The Analysis Pipeline |
21 | 18 |
|
22 | | -4. **Phase 4: Decision Synthesis** |
23 | | - The final phase integrates the results from the previous three. It prioritizes the verdicts from the static and symbolic phases and uses the dynamic tracing result as the final arbiter if the code's behavior could not be proven formally. |
| 19 | +The analyzer processes scripts through the following sequence: |
24 | 20 |
|
25 | | ---- |
| 21 | +#### Meta-Analysis: Cycle & Paradox Detection |
| 22 | +Before the main analysis begins, two crucial meta-checks are performed to protect the analyzer itself from paradoxical attacks. |
26 | 23 |
|
27 | | -## How to Run the Analyzer |
| 24 | +1. **Semantic Hashing (`semantic_hashing.py`):** Instead of a simple lexical hash of the code, the analyzer first converts the program into a **canonical form**. This process uses an Abstract Syntax Tree (AST) transformer to rename all variables, functions, and arguments to a standard format (`func_0`, `var_0`, etc.) and remove comments. This ensures that two programs that are structurally identical but use different names will produce the **same hash**. |
28 | 25 |
|
29 | | -The `main.py` script is configured to run as a test harness, automatically analyzing all scripts found in the `/scripts` directory. |
| 26 | +2. **Cross-Script Cycle Detection (`cross_script_recursion.py`):** The analyzer maintains a chain of the semantic hashes of every program currently under analysis. If it is asked to analyze a script whose semantic hash is already in the chain (e.g., A analyzes B, which analyzes a cosmetically different version of A), a mutual recursion cycle is detected and the analysis is short-circuited. |
30 | 27 |
|
31 | | -To run the full analysis suite, simply execute the main script: |
| 28 | +#### Phase 0: Adversarial Pattern Matching (`paradox_detection.py`) |
| 29 | +* **Purpose:** To identify specific, known implementations of the classic halting problem paradox. |
| 30 | +* **Method:** Uses a highly specific AST visitor to look for the exact structure of a program that reads its own source, calls the analyzer on itself, and inverts the result. |
| 31 | + |
| 32 | +#### Phase 1: Static Analysis (`static_analysis.py`) |
| 33 | +* **Purpose:** The fastest check for the most obvious cases. |
| 34 | +* **Method:** Walks the AST to find definitive conditions. |
| 35 | + * **Finds `while True:`:** Immediately returns `does not halt`. |
| 36 | + * **Finds no loops AND no recursion:** Immediately returns `halts`. |
| 37 | + * **Finds loops or recursion it cannot solve:** Defers to the next phase. |
| 38 | + |
| 39 | +#### Phase 2: Symbolic Prover (`symbolic_prover.py`) |
| 40 | +* **Purpose:** To handle common loop structures that are too complex for the basic static analyzer but can still be proven without full execution. |
| 41 | +* **Method:** Uses AST analysis to prove termination for a wider class of loops. |
| 42 | + * **Identifies `for i in range(constant)`:** Returns `halts`. |
| 43 | + * **Identifies `while var < constant:` with a clear increment (`var = var + const`):** Returns `halts`. |
| 44 | + |
| 45 | +#### Phase 3: Dynamic Tracing (`dynamic_tracing.py`) |
| 46 | +* **Purpose:** The most powerful and expensive phase. It executes the code in a monitored environment to observe its behavior directly. |
| 47 | +* **Method:** |
| 48 | + * **Blunt Check:** First checks for the literal string `"analyze_halting"` in the code, providing a fast exit for most self-referential scripts. |
| 49 | + * **Execution Tracing:** If the blunt check fails, it executes the code line by line, monitoring for: |
| 50 | + * **Infinite Recursion:** A recursion depth limit that, when exceeded, signals a non-halting state. |
| 51 | + * **Execution Trace Cycling:** Detects if the program enters a state (line number and local variables) that it has been in before, indicating a non-terminating loop. |
| 52 | + |
| 53 | +## The Gauntlet: A Showcase of Defeated Paradoxes |
| 54 | + |
| 55 | +The `/scripts` directory contains a suite of test cases designed to challenge each layer of the analyzer's defenses. |
| 56 | + |
| 57 | +* `non_halting.py`: Defeated by **Phase 1 (Static Analysis)**. |
| 58 | +* `bounded_loop.py`: Defeated by **Phase 2 (Symbolic Prover)**. |
| 59 | +* `paradox.py`: Defeated by **Phase 0 (Pattern Matching)**. |
| 60 | +* `obfuscated_paradox.py`: Defeated by **Phase 3 (Dynamic Tracing's blunt check)**. |
| 61 | +* `final_paradox.py`: Defeated by the **Cross-Script Cycle Detector** (direct `A->A` recursion). |
| 62 | +* `mutating_paradox_*.py`: Defeated by **Phase 3 (Dynamic Tracing's blunt check)**. |
| 63 | +* `semantic_paradox_A.py`: Defeated by the **Semantic Hashing + Cycle Detector** (`A->B->C(A-like)` recursion). |
| 64 | +* `polymorphic_termination_paradox.py`: The ultimate test, defeated by the **Symbolic Prover's** ability to resolve the inner dilemma, which then allows the **Dynamic Tracer** to catch the outer paradoxical payload. |
| 65 | + |
| 66 | +## Usage |
| 67 | + |
| 68 | +To run the analysis on all test scripts, simply execute `main.py` from your terminal: |
32 | 69 |
|
33 | 70 | ```bash |
34 | 71 | python main.py |
35 | 72 | ``` |
36 | 73 |
|
37 | | -The analyzer will then process each file and print a detailed report of its findings for each one. |
| 74 | +The analyzer will process each file in the `/scripts` directory and print the result. Use the provided cleanup scripts to remove any files generated during the tests. |
| 75 | + |
| 76 | +```bash |
| 77 | +# Example cleanup |
| 78 | +python cleanup_prover_test.py |
| 79 | +``` |
38 | 80 |
|
39 | | ---- |
| 81 | +## The Never-Ending Game: Limitations and Philosophy |
40 | 82 |
|
41 | | -## Disclaimer |
| 83 | +While this analyzer is robust, the Halting Problem remains undecidable. No set of heuristics is perfect. An adversary could, in theory, design a paradox based on a level of semantic equivalence that even the symbolic prover cannot solve (e.g., a complex mathematical calculation vs. a simple loop that both happen to run for the same number of iterations). |
42 | 84 |
|
43 | | -This tool is an engineering solution, not a theoretical one. It **does not solve the Halting Problem**, which is proven to be impossible. It is a powerful heuristic designed to provide a correct answer for a large class of practical programs. It can and will fail on programs with undetectable infinite loops or programs whose behavior depends on unpredictable external input. |
| 85 | +This project's philosophy is not to achieve theoretical perfection, but to demonstrate a practical, layered approach that pushes the boundary of what can be decided, catching increasingly sophisticated and realistic non-halting scenarios. |
0 commit comments