Adds automatic running, formula tests

This commit is contained in:
LSaldyt
2017-10-04 15:20:59 -06:00
parent 7abb40f849
commit b90bae2584
3 changed files with 129 additions and 22 deletions

View File

@ -4,7 +4,6 @@ from .slipnet import Slipnet
from .temperature import Temperature from .temperature import Temperature
from .workspace import Workspace from .workspace import Workspace
class Reporter(object): class Reporter(object):
"""Do-nothing base class for defining new reporter types""" """Do-nothing base class for defining new reporter types"""
def report_answer(self, answer): def report_answer(self, answer):
@ -66,24 +65,36 @@ class Copycat(object):
self.reporter.report_answer(answer) self.reporter.report_answer(answer)
return answer return answer
def run(self, initial, modified, target, iterations): def run(self, initial, modified, target, iterations, testAdjFormulas=False):
self.workspace.resetWithStrings(initial, modified, target) self.workspace.resetWithStrings(initial, modified, target)
answers = {} if testAdjFormulas:
for i in range(iterations): formulas = self.temperature.adj_formulas()
answer = self.runTrial() else:
d = answers.setdefault(answer['answer'], { formulas = ['original']
'count': 0,
'sumtemp': 0, # TODO: use entropy
'sumtime': 0
})
d['count'] += 1
d['sumtemp'] += answer['temp'] # TODO: use entropy
d['sumtime'] += answer['time']
for answer, d in answers.items(): formulaList = []
d['avgtemp'] = d.pop('sumtemp') / d['count'] for formula in formulas:
d['avgtime'] = d.pop('sumtime') / d['count'] self.temperature.useAdj(formula)
return answers answers = {}
for i in range(iterations):
answer = self.runTrial()
d = answers.setdefault(answer['answer'], {
'count': 0,
'sumtemp': 0, # TODO: use entropy
'sumtime': 0
})
d['count'] += 1
d['sumtemp'] += answer['temp'] # TODO: use entropy
d['sumtime'] += answer['time']
for answer, d in answers.items():
d['avgtemp'] = d.pop('sumtemp') / d['count']
d['avgtime'] = d.pop('sumtime') / d['count']
formulaList.append(answers)
if not testAdjFormulas:
return formulaList[0]
else:
return formulaList
def run_forever(self, initial, modified, target): def run_forever(self, initial, modified, target):
self.workspace.resetWithStrings(initial, modified, target) self.workspace.resetWithStrings(initial, modified, target)

View File

@ -1,8 +1,44 @@
import math import math
# Alternate formulas for getAdjustedProbability
def _original(temp, prob):
if prob == 0 or prob == 0.5 or temp == 0:
return prob
if prob < 0.5:
return 1.0 - _original(temp, 1.0 - prob)
coldness = 100.0 - temp
a = math.sqrt(coldness)
c = (10 - a) / 100
f = (c + 1) * prob
return max(f, 0.5)
def _entropy(temp, prob):
if prob == 0 or prob == 0.5 or temp == 0:
return prob
if prob < 0.5:
return 1.0 - _original(temp, 1.0 - prob)
coldness = 100.0 - temp
a = math.sqrt(coldness)
c = (10 - a) / 100
f = (c + 1) * prob
return -f * math.log2(f)
def _inverse_prob(temp, prob):
# Temperature, potentially clamped
iprob = 1 - prob
return (temp / 100) * iprob + ((100 - temp) / 100) * prob
class Temperature(object): class Temperature(object):
def __init__(self): def __init__(self):
self.reset() self.reset()
self.adjustmentType = 'original'
self._adjustmentFormulas = {
'original' : _original,
'entropy' : _entropy,
'inverse' : _inverse_prob
}
def reset(self): def reset(self):
self.actual_value = 100.0 self.actual_value = 100.0
@ -32,7 +68,19 @@ class Temperature(object):
def getAdjustedValue(self, value): def getAdjustedValue(self, value):
return value ** (((100.0 - self.value()) / 30.0) + 0.5) return value ** (((100.0 - self.value()) / 30.0) + 0.5)
""" def getAdjustedProbability(self, value):
temp = self.value()
prob = value
return self._adjustmentFormulas[self.adjustmentType](temp, prob)
def useAdj(self, adj):
print('Changing to adjustment formula {}'.format(adj))
self.adjustmentType = adj
def adj_formulas(self):
return self._adjustmentFormulas.keys()
'''
def getAdjustedProbability(self, value): def getAdjustedProbability(self, value):
if value == 0 or value == 0.5 or self.value() == 0: if value == 0 or value == 0.5 or self.value() == 0:
return value return value
@ -43,9 +91,20 @@ class Temperature(object):
c = (10 - a) / 100 c = (10 - a) / 100
f = (c + 1) * value f = (c + 1) * value
return max(f, 0.5) return max(f, 0.5)
"""
def getAdjustedProbability(self, value): def getAdjustedProbability(self, value):
if value == 0 or value == 0.5 or self.value() == 0:
return value
if value < 0.5:
return 1.0 - self.getAdjustedProbability(1.0 - value)
coldness = 100.0 - self.value()
a = math.sqrt(coldness)
c = (10 - a) / 100
f = (c + 1) * value
# return max(f, 0.5)
# return max(f, 0.0)
# return (0 + (-f * math.log2(f)))
return -f * math.log2(f)
# TODO: use entropy # TODO: use entropy
""" """
@ -291,9 +350,13 @@ class Temperature(object):
# This function does precisely what I think the original lisp comment describes, but provides crappy results # This function does precisely what I think the original lisp comment describes, but provides crappy results
# return (temp / 200) * iprob + ((200 - temp) / 200) * prob # return (temp / 200) * iprob + ((200 - temp) / 200) * prob
# However, this version preforms much better: # However, this version preforms much better:
# Essentially, it weights probabilities towards their inverses when temperature is higher, and leaves them unaffected when it is lower.
# Some statistical analysis is needed of course # Some statistical analysis is needed of course
return (temp / 100) * iprob + ((100 - temp) / 100) * prob return (temp / 100) * iprob + ((100 - temp) / 100) * prob
'''
# This will give only xyd answers:
#return 1 - (temp / 100) * iprob + ((100 - temp) / 100) * prob
"""
lucas@infinity:~/projects/personal/copycat$ ./main.py abc abd xyz --iterations 10 --plot lucas@infinity:~/projects/personal/copycat$ ./main.py abc abd xyz --iterations 10 --plot
Answered wyz (time 3865, final temperature 13.9) Answered wyz (time 3865, final temperature 13.9)
Answered wyz (time 8462, final temperature 10.8) Answered wyz (time 8462, final temperature 10.8)
@ -324,7 +387,7 @@ class Temperature(object):
Answered ijjkkl (time 1064, final temperature 50.6) Answered ijjkkl (time 1064, final temperature 50.6)
ijjlll: 3 (avg time 3051.0, avg temp 16.1) ijjlll: 3 (avg time 3051.0, avg temp 16.1)
ijjkkl: 7 (avg time 1540.4, avg temp 42.6) ijjkkl: 7 (avg time 1540.4, avg temp 42.6)
''' """
# unparameterized version # unparameterized version
#curvedProb = (temp / 100) * iprob + (cold / 100) * prob #curvedProb = (temp / 100) * iprob + (cold / 100) * prob
@ -340,7 +403,7 @@ class Temperature(object):
# beta = 1.0 # beta = 1.0
# return ((alpha + temp / 100) * iprob + (beta + cold / 100) * prob) / (alpha + beta) # return ((alpha + temp / 100) * iprob + (beta + cold / 100) * prob) / (alpha + beta)
''' """
# A scaling factor (between 0 and infinity), based on temperature (i.e. 100/coldness) # A scaling factor (between 0 and infinity), based on temperature (i.e. 100/coldness)
if temp == 100: # Avoid dividing by zero if temp == 100: # Avoid dividing by zero
factor = float('inf') factor = float('inf')

33
multi-run.py Executable file
View File

@ -0,0 +1,33 @@
#!/usr/bin/env python3
import argparse, logging
from copycat import Copycat, Reporter, plot_answers, save_answers
class SimpleReporter(Reporter):
"""Reports results from a single run."""
def report_answer(self, answer):
"""Self-explanatory code."""
print('Answered %s (time %d, final temperature %.1f)' % (
answer['answer'], answer['time'], answer['temp'],
))
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--iterations', type=int, default=1, help='Run the given case this many times.')
options = parser.parse_args()
copycat = Copycat(reporter=SimpleReporter())
with open('input/reduced_problems.csv', 'r') as infile:
for line in infile:
line = line.replace('\n', '')
a, b, c = line.split(',')
answerList = copycat.run(a, b, c, options.iterations, True)
for answers in answerList:
for answer, d in sorted(iter(answers.items()), key=lambda kv: kv[1]['avgtemp']):
print('%s: %d (avg time %.1f, avg temp %.1f)' % (answer, d['count'], d['avgtime'], d['avgtemp']))
#filename = 'output/{}-{}-{}.csv'.format(a, b, c)
#save_answers(answers, filename)
if __name__ == '__main__':
main()