Universal Computer Programs

Gregory M. Kapfhammer

October 6, 2025

What makes a program “universal”? Can any program execute any other program?

  • Explore universal Python programs and Turing machines
  • Develop automated program alteration techniques

One programs runs another

  • Python program: Python interpreter runs any program
  • Web browser:
    • Browser renders any web page
    • Browser runs any JavaScript program
    • Browser runs any WebAssembly program
  • Operating system: runs any executable program
  • Isn’t it magical that one program can run another?
  • Why is this possible? What are the benefits?

Running Python programs

  • Python’s built-in capability
    • exec() function executes Python code from strings
    • Any programming language can execute its own code
    • Python makes this particularly easy to implement
    • Foundation for creating universal programs
  • Simple example in action
    • command = "print('abc', 5+2)"
    • exec(command) produces output abc 7
    • The parameter 5+2 gets evaluated to 7
    • String becomes executable Python code

Running Python code strings

def universal(prog_string, in_string):
    """Execute any Python program with any input."""
    # Execute the definition of the function in prog_string
    # This defines the function but doesn't invoke it
    namespace = {}
    exec(prog_string, namespace)
    # Extract function name from the program string
    # Find the first callable function in namespace
    for name, obj in namespace.items():
        if callable(obj) and not name.startswith('_'):
            return obj(in_string)
    return "No function found"

# Test the universal program
prog = """
def contains_gaga(inString):
    return 'yes' if 'GAGA' in inString else 'no'
"""

print(universal(prog, 'GTTGAGA'))  # Should output: yes
print(universal(prog, 'GTTAA'))    # Should output: no
yes
no
  • Key insight: Universal programs can simulate any other program!

What makes this universal?

  • Unlimited simulation power
    • Can execute any syntactically valid Python program
    • Works with programs of arbitrary complexity
    • No restrictions on program logic or structure
    • Only limited by Python language capabilities
  • Three-step process
    • Define: exec() creates function in memory
    • Extract: Get reference to the newly defined function
    • Execute: Call function with provided input
    • Result matches original program’s behavior

Universal computation examples

  • Hardware: containsGAGA booted as machine code
  • Operating system: run the program containsGAGA.exe
  • Python program: interpreter runs containsGAGA.py
  • Java virtual machine: interpreter runs containsGAGA.class
  • Web browser: interpreter runs containsGAGA.js
  • Web browser: interpreter runs containsGAGA.wasm

These examples all suggest that universal computation is complicated! However, it is not! Let’s look at some simple examples!

Universal Turing machines

  • First universal Turing machine had
    • Only dozens of states
    • About 20 symbols in the alphabet
  • Revised Minsky universal Turing machine had
    • Only 7 states
    • Only 4 symbols in the alphabet
  • Insight: Universality does not require great complexity!
  • Trade-off: small universal machines may require long machine descriptions and often lead to inefficient simulations

Universal computation examples

  • Rule 110 cellular automaton
    • Uses only 2 symbols and 8 simple rules
    • Proven to be computationally universal
    • Can simulate any computation given proper encoding
    • Extremely simple yet infinitely powerful
  • Conway’s Game of Life
    • Simple rules govern cell birth, death, survival
    • Turing-complete: can compute anything computable
    • Universal computation emerges from local interactions
    • Potential natural occurrences in molecular systems

What’s next after a universal program?

  • Explore program alteration techniques
    • One program inputs another program and its input
    • The inputting program modifies behavior of the one input
    • Many practical benefits in areas like software engineering
    • Simulate-and-alter aids proofs of uncomputability

Program alteration techniques

Visualize a program to ignore input

Levels of computational abstraction

Analysis Type Program Parameters
Analyze a string containsGAGA.py "ATGAG"
Analyze another program countLines.py containsGAGA.py
Simulate another program universal.py (containsGAGA.py, "ATGAG")
Simulate altered program alterGAGAtoTATA.py Modified repeatCAorGA.py
  • Progression: String analysis, program simulation, program alteration
  • Difficulty: String analysis is easiest, program alteration is hardest
  • Power: Simulating an altered program provides a powerful tool

Recognizable versus decidable

  • Recognizable means that a language or decision problem can correctly decide all positive instances. In contrast, decidable means that a language or decision problem can correctly decide all instances, both positive and negative. Remember, recognizable is “easier” than decidable!

  • Summary of recognizable problems

    • Program correctly handles all positive instances
    • May loop forever on negative instances
    • yesOnString and crashOnString are recognizable
    • Universal simulation enables recognition
  • Let’s look at some examples of recognizable problems!

  • Do you understand why this does not demonstrate decidability?

Explore recYesOnString

import utils; from utils import rf
from universal import universal  
def recYesOnString(inString):
    (progString, newInString) = utils.DESS(inString) 
    val = universal(progString, newInString)  
    if val == 'yes':
        return 'yes'
    else:
        return 'no'  
  • Does this program recognize yesOnString? Yes!
  • Does this program decide yesOnString? No!
  • It may loop forever on some negative instances
  • Infinite loop is possible on the call to universal()

Why universal computation matters

  • Theoretical foundations
    • Proves existence of problems beyond computation
    • Shows limits of what any computer can solve
    • Enables rigorous mathematical analysis of computation
    • Foundation for complexity theory and decidability
  • Practical implications
    • Interpreters, compilers, virtual machines
    • Emulation and simulation systems
    • Program analysis and transformation tools
    • Understanding computational boundaries

Looking ahead to problem reductions

  • Next, we will explore reductions:
    • Transform one problem into another
    • Reductions for easiness versus for hardness
    • Show that solving one solves the other
    • Key technique in computability and complexity theory
    • Aid proving undecidability and NP-completeness

Course learning objectives

Learning Objectives for Theoretical Machines

  • CS-204-1: Use both intuitive analysis and theoretical proof techniques to correctly distinguish between problems that are tractable, intractable, and uncomputable.
  • CS-204-2: Correctly use one or more variants of the Turing machine (TM) abstraction to both describe and analyze the solution to a computational problem.
  • CS-204-3: Correctly use one or more variants of the finite statement machine (FSM) abstraction to describe and analyze the solution to a computational problem.
  • CS-204-4: Use a formal proof technique to correctly classify a problem according to whether or not it is in the P, NP, NP-Hard, and/or NP-Complete complexity class(es).
  • CS-204-5: Apply insights from theoretical proofs concerning the limits of either program feasibility or complexity to the implementation of both correct and efficient real-world Python programs.