October 13, 2025
Learning Objectives for Theoretical Machines
import utils; from utils import rf
from lastDigitIsEven import lastDigitIsEven
def isOddViaReduction(inString):
inStringPlusOne = int(inString) + 1
return lastDigitIsEven(str(inStringPlusOne))
def testisOddViaReduction():
testVals = [('-2', 'no'),
('0', 'no'),
('2', 'no'),
('3742788', 'no'),
('-1', 'yes'),
('1', 'yes'),
('3', 'yes'),
('17', 'yes'),
('3953969', 'yes'),
]
for (inString, solution) in testVals:
val = isOddViaReduction(inString)
utils.tprint(inString, ':', val)
assert val == solution
isOddViaReduction
uses lastDigitIsEven
to solve the isOdd
problemisOddViaReduction
solves isOdd
by “reducing” it to lastDigitIsEven
lastDigitIsEven
functionimport utils; from utils import rf
def lastDigitIsEven(inString):
lastDigit = inString[-1] # ``[-1]'' is Python notation for last element
if lastDigit in '02468':
return 'yes'
else:
return 'no'
def testlastDigitIsEven():
testVals = [('-2', 'yes'),
('0', 'yes'),
('2', 'yes'),
('3742788', 'yes'),
('-1', 'no'),
('1', 'no'),
('3', 'no'),
('17', 'no'),
('3953969', 'no'),
]
for (inString, solution) in testVals:
val = lastDigitIsEven(inString)
utils.tprint(inString, ':', val)
assert val == solution
lastDigitIsEven
function can solve the isOdd
problemisOddViaReduction
behaves the same way as the isOdd
functionYesOnString
:
ContainsGAGA
is uncomputableHaltsOnString
is uncomputableYesOnString
\(\rightarrow\) ContainsGAGA
ContainsGAGA
is uncomputableYesOnString
is uncomputableYesOnString
\(\leq_T\) ContainsGAGA
YesOnString
: Given program \(P\) and input \(I\), does \(P(I)=\) “yes”?ContainsGAGA
: Given program \(P\) and input \(I\), does \(P(I)\) contain the string “GAGA”?YesOnString
into an instance of ContainsGAGA
(i.e., ContainsGAGA
will compute YesOnString
)ContainsGAGA
to solve the YesOnString
problemyesViaGAGA
programimport utils; from utils import rf
from GAGAOnString import GAGAOnString # oracle function
def yesViaGAGA(progString, inString):
singleString = utils.ESS(progString, inString)
return GAGAOnString(rf('alterYesToGAGA.py'), singleString)
def testYesViaGAGA():
testvals = [
('containsGAGA.py', 'TTTTGAGATT', 'yes'),
('containsGAGA.py', 'TTTTGAGTT', 'no'),
('isEmpty.py', '', 'yes'),
('isEmpty.py', 'x', 'no'),
]
for (filename, inString, solution) in testvals:
val = yesViaGAGA(rf(filename), inString)
utils.tprint(filename + ":", val)
assert val == solution
YesOnString
problem using an “oracle” for ContainsGAGA
, meaning we will solve known problem with new oneGAGAOnString
programimport utils; from utils import rf
from universal import universal
def GAGAOnString(progString, inString):
val = universal(progString, inString)
if val == 'GAGA':
return 'yes'
else:
return 'no'
def testGAGAOnString():
testvals = [('containsGAGA.py', 'GAGAGAGAG', 'no'),
('repeatCAorGA.py', 'CA', 'no'),
('repeatCAorGA.py', 'GA', 'yes') ]
for (progName, inString, solution) in testvals:
val = GAGAOnString(rf(progName), inString)
utils.tprint( (progName, inString), ":", val )
assert val == solution
GAGAOnString
function transforms the output of any program to “yes” if standard output equals “GAGA” or “no” otherwisealterYesToGAGA
programimport utils; from utils import rf
from universal import universal
def alterYesToGAGA(inString):
(progString, newInString) = utils.DESS(inString)
val = universal(progString, newInString)
if val == 'yes':
return 'GAGA'
else:
return 'no'
def testAlterYesToGAGA():
for (progName, inString, solution) in [('containsGAGA.py', 'GAGAGAGAG', 'GAGA'),
('containsGAGA.py', 'ATATACCC', 'no') ]:
progString = rf(progName)
combinedString = utils.ESS(progString, inString)
val = alterYesToGAGA(combinedString)
utils.tprint( (progName, inString), ":", val )
assert val == solution
alterYesToGAGA
function transforms the output of any program to either “GAGA” if output is “yes” or “no” otherwiseYesOnString
\(\leq_T\) ContainsGAGA
yesViaGAGA
: uses the GAGAOnString
(i.e., ContainsGAGA
) oracle to decide YesOnString
by calling the oracle on the transformed program.
yesViaGAGA
is functionally equivalent to YesOnString
yesViaGAGA
uses GAGAOnString
as a key building blockalterYesToGAGA.py
: transforms an instance (P, I)
so that if P(I)
returns "yes"
the transformed program prints "GAGA"
, otherwise prints "no"
.
GAGAOnString
: oracle wrapper that simulates a program and returns "yes"
iff the program’s standard output equals GAGA
.
Key insights about this proof technique using Turing reductions:
ContainsGAGA
’s uncomputabilityYesOnString
\(\leq_T\) (“reduces to”) ContainsGAGA
YesOnString
is no harder than ContainsGAGA
ContainsGAGA
is uncomputable?ContainsGAGA
is computableYesOnString
is computable, via the reductionYesOnString
is uncomputable, which we have previously proven in a separate proof by contradiction!ContainsGAGA
was wrongContainsGAGA
must be uncomputable via reductionContainsGAGA
is uncomputable!YesOnString
, previously proven uncomputable
YesOnEmpty
(does \(P(\epsilon)=\) “yes”?)YesOnAll
(does \(P(I)=\) “yes” for all \(I\)?)YesOnSome
(does \(P(I)=\) “yes” for some \(I\)?)ContainsGAGA
(does \(P(I)\) output contain “GAGA”?)HaltsOnString
, HaltsOnEmpty
, HaltsOnAll
NumCharsOnString
, NumStepsOnString
Key: Importantly, all of these uncomputability proofs start from a single problem, such as YesOnString
, that we’ve proven to be uncomputable!
Building hierarchies: Transitivity helps organize problems by hardness
Viewing hierarchies: See the relationships between problems in a tree
Tree Interpretation: Node at the top is the “starting point” of the Turing reduction chain! Okay, let’s see what this uncomputability tree looks like!
YesOnString
is no harder than HaltsOnString
HaltsOnString
is no harder than HaltsOnEmpty
HaltsOnString
is no harder than NumStepsOnString
YesOnString
is no harder than HaltsOnEmpty
YesOnString
is no harder than NumStepsOnString
YesOnString
to YesOnEmpty
import utils; from utils import rf
from yesOnEmpty import yesOnEmpty # oracle function
def yesViaEmpty(progString, inString):
utils.writeFile('progString.txt', progString)
utils.writeFile('inString.txt', inString)
return yesOnEmpty(rf('ignoreInput.py'))
YesOnString
\(\leq_T\) YesOnEmpty
YesOnString
is no harder than YesOnEmpty
YesOnEmpty
is undecidableHaltsOnString
is undecidable?yesViaHalt
programimport utils; from utils import rf
from haltsOnString import haltsOnString # oracle function
def yesViaHalts(progString, inString):
singleStr = utils.ESS(progString, inString)
return haltsOnString(rf('alterYesToHalt.py'), singleStr)
def testYesViaHalts():
testvals = [
('containsGAGA.py', 'TTTTGAGATT', 'yes'),
('containsGAGA.py', 'TTTTGAGTT', 'no'),
('isEmpty.py', '', 'yes'),
('isEmpty.py', 'x', 'no'),
]
for (filename, inString, solution) in testvals:
val = yesViaHalts(rf(filename), inString)
utils.tprint(filename + ":", val)
assert val == solution
haltsOnString
“oracle” to solve YesOnString
, meaning YesOnString
\(\leq_T\) HaltsOnString
. Next, what is the implementation of haltsOnString
?haltsOnString
programimport utils; from utils import rf
from universal import universal
def haltsOnString(progString, inString):
# if it doesn't complete before timing out, assume it is in an infinite loop
# note that this is for demonstration purposes only and not how an
# actual "haltsOnString" oracle would be implemented in the proof
val = utils.runWithTimeout(None, universal, progString, inString)
if val != None:
return 'yes'
else:
return 'no'
def testhaltsOnString():
for (progName, inString, solution) in [
('loopIfContainsGAGA.py', 'GAGAGAGAG', 'no'), \
('loopIfContainsGAGA.py', 'TTTTGGCCGGT', 'yes') ]:
val = haltsOnString(rf(progName), inString)
utils.tprint( (progName, inString), ":", val )
assert val == solution
runWithTimeout
to simulate halting behavior since the universal
program may run forever with programs having infinite loopsalterYesToHalt
programimport utils; from utils import rf
from universal import universal
def alterYesToHalt(inString):
(progString, newInString) = utils.DESS(inString)
val = universal(progString, newInString)
if val == 'yes':
# return value is irrelevant, since returning any string halts
return 'halted'
else:
# deliberately enter infinite loop
utils.loop()
def testAlterYesToHalt():
for (progName, inString, solution) in [
('containsGAGA.py', 'GAGAGAGAG', 'halted'),
('containsGAGA.py', 'TTTTGGCCGGT', None),
]:
combinedString = utils.ESS(rf(progName), inString)
val = utils.runWithTimeout(None, alterYesToHalt, combinedString)
utils.tprint( (progName, inString), ":", val )
assert val == solution
alterYesToHalt
program returns 'halted'
if \(P(I)=\)'yes'
and otherwise enters an infinite loop using the utility function utils.loop()
functionYesOnString
\(\leq_T\) HaltsOnString
yesViaHalts
: uses the haltsOnString
oracle to decide YesOnString
by calling the oracle on the transformed program.
yesViaHalts
is functionally equivalent to YesOnString
yesViaHalts
uses haltsOnString
as a key building blockalterYesToHalt.py
: transforms an instance (P, I)
so that if P(I)
returns "yes"
the transformed program halts (returns), otherwise it enters an infinite loop.
haltsOnString
: oracle “wrapper” that simulates a program with timeout and returns "yes"
iff the program halts within the timeout period.
Key insights about this proof technique using Turing reductions:
HaltsOnString
’s uncomputabilityYesOnString
\(\leq_T\) (“reduces to”) HaltsOnString
YesOnString
is no harder than HaltsOnString
YesOnString
, many problems are uncomputable!Proofgrammers