Major overhaul of temperature logic. Behavioral change.

I think the reason the temperature logic was so confused in the old code
is because the Java code has a class `Temperature` that is used for
graphical display *and* two variables in `formulas` that are used for
most of the actual math. But somewhere along the line, some of the code
in `formulas.java` started reading from `Temperature.value` as well.
So the Python code was just faithfully copying that confusion.

The actual abstraction here is a very simple "temperature" object
with a stored value. It can be "clamped" to 100.0 for a given period.
The only complication is that one of the codelets (the rule-transformer
codelet) wants to get access to the "actual value" of the temperature
even when it is clamped.

The Python rule-transformer codelet also had a bug: it was accidentally
setting `temperature.value` on the `temperature` module instead of on
the `temperature.temperature` object! This turned some of its behavior
into a no-op, for whatever that's worth.

Lastly, the calculation of `finalTemperature` in the main program can
now report 100.0 if the answer is found while the temperature is clamped.
I don't fully understand why this didn't happen in the old code.
I've hacked around it with `temperature.last_unclamped_value` for now,
but I should TODO FIXME.
This commit is contained in:
Arthur O'Dwyer
2017-04-17 01:29:16 -07:00
parent 6a56fdd898
commit cc288161a4
7 changed files with 34 additions and 40 deletions

View File

@ -3,7 +3,7 @@ import logging
import random
from slipnet import slipnet
import temperature
from temperature import temperature
import formulas
from workspaceFormulas import chooseDirectedNeighbor
from workspaceFormulas import chooseNeighbor
@ -130,7 +130,7 @@ def __slippability(conceptMappings):
@codelet('breaker')
def breaker(coderack, codelet):
probabilityOfFizzle = (100.0 - formulas.Temperature) / 100.0
probabilityOfFizzle = (100.0 - temperature.value()) / 100.0
if formulas.coinFlip(probabilityOfFizzle):
return
# choose a structure at random
@ -804,13 +804,11 @@ def rule_translator(coderack, codelet):
if bondDensity > 1.0:
bondDensity = 1.0
cutoff = __getCutOff(bondDensity) * 10.0
assert cutoff >= formulas.actualTemperature
if workspace.rule.buildTranslatedRule():
workspace.foundAnswer = True
else:
temperature.clampTime = coderack.codeletsRun + 100
temperature.clamped = True
formulas.Temperature = 100.0
if cutoff >= temperature.actual_value:
if workspace.rule.buildTranslatedRule():
workspace.foundAnswer = True
else:
temperature.clampUntil(coderack.codeletsRun + 100)
@codelet('bottom-up-correspondence-scout')

View File

@ -23,7 +23,7 @@ def probabilityOfPosting(workspace, codeletName):
if codeletName == 'breaker':
return 1.0
if 'description' in codeletName:
result = (formulas.Temperature / 100.0) ** 2
result = (temperature.value() / 100.0) ** 2
else:
result = workspace.intraStringUnhappiness / 100.0
if 'correspondence' in codeletName:
@ -165,7 +165,7 @@ class Coderack(object):
urgency = 3
if codeletName == 'breaker':
urgency = 1
if formulas.Temperature < 25.0 and 'translator' in codeletName:
if temperature.value() < 25.0 and 'translator' in codeletName:
urgency = 5
for _ in xrange(howMany):
if formulas.coinFlip(probability):
@ -303,7 +303,7 @@ class Coderack(object):
def chooseCodeletToRun(self):
assert self.codelets
scale = (100.0 - formulas.Temperature + 10.0) / 15.0
scale = (100.0 - temperature.value() + 10.0) / 15.0
urgsum = sum(codelet.urgency ** scale for codelet in self.codelets)
threshold = random.random() * urgsum
chosen = self.codelets[0]

View File

@ -17,7 +17,7 @@ def mainLoop(lastUpdate):
workspace.updateEverything()
coderack.updateCodelets()
slipnet.update()
workspace.updateTemperature()
temperature.update(workspace.getUpdatedTemperature())
lastUpdate = currentTime
logging.debug('Number of codelets: %d', len(coderack.codelets))
coderack.chooseAndRunCodelet()
@ -36,7 +36,7 @@ def runTrial(answers):
answer = workspace.rule.finalAnswer
else:
answer = None
finalTemperature = temperature.value
finalTemperature = temperature.last_unclamped_value
finalTime = coderack.codeletsRun
print 'Answered %s (time %d, final temperature %.1f)' % (answer, finalTime, finalTemperature)
answers[answer] = answers.get(answer, {'count': 0, 'tempsum': 0, 'timesum': 0})

View File

@ -4,8 +4,6 @@ import random
from temperature import temperature
actualTemperature = Temperature = 100.0
def selectListPosition(probabilities):
total = sum(probabilities)
@ -35,23 +33,18 @@ def weightedAverage(values):
def temperatureAdjustedValue(value):
#logging.info('Temperature: %s' % Temperature)
#logging.info('actualTemperature: %s' % actualTemperature)
return value ** (((100.0 - Temperature) / 30.0) + 0.5)
return value ** (((100.0 - temperature.value()) / 30.0) + 0.5)
def temperatureAdjustedProbability(value):
if not value or value == 0.5 or not temperature.value:
if value == 0 or value == 0.5 or temperature.value() == 0:
return value
if value < 0.5:
return 1.0 - temperatureAdjustedProbability(1.0 - value)
coldness = 100.0 - temperature.value
coldness = 100.0 - temperature.value()
a = math.sqrt(coldness)
b = 10.0 - a
c = b / 100
d = c * (1.0 - (1.0 - value)) # as said the java
e = (1.0 - value) + d
f = 1.0 - e
c = (10 - a) / 100
f = (c + 1) * value
return max(f, 0.5)

View File

@ -1,19 +1,29 @@
import logging
class Temperature(object):
def __init__(self):
self.value = 100.0
self.actual_value = 100.0
self.last_unclamped_value = 100.0
self.clamped = True
self.clampTime = 30
def update(self, value):
self.value = value
self.last_unclamped_value = value
if self.clamped:
self.actual_value = 100.0
else:
self.actual_value = value
def clampUntil(self, when):
self.clamped = True
self.clampTime = when
# but do not modify self.actual_value until someone calls update()
def tryUnclamp(self, currentTime):
if self.clamped and currentTime >= self.clampTime:
logging.info('unclamp temperature at %d', currentTime)
self.clamped = False
def value(self):
return 100.0 if self.clamped else self.actual_value
temperature = Temperature()

View File

@ -1,7 +1,6 @@
import logging
import formulas
from temperature import temperature
from workspaceString import WorkspaceString
@ -85,19 +84,14 @@ class Workspace(object):
self.initial.updateIntraStringUnhappiness()
self.target.updateIntraStringUnhappiness()
def updateTemperature(self):
def getUpdatedTemperature(self):
self.assessTemperature()
ruleWeakness = 100.0
if self.rule:
self.rule.updateStrength()
ruleWeakness = 100.0 - self.rule.totalStrength
values = ((self.totalUnhappiness, 0.8), (ruleWeakness, 0.2))
above_actual_temperature = formulas.actualTemperature + 0.001
formulas.actualTemperature = formulas.weightedAverage(values)
if temperature.clamped:
formulas.actualTemperature = 100.0
formulas.Temperature = formulas.actualTemperature
temperature.update(formulas.Temperature)
return formulas.weightedAverage(values)
def numberOfUnrelatedObjects(self):
"""A list of all objects in the workspace with >= 1 open bond slots"""

View File

@ -1,7 +1,6 @@
import logging
from workspace import workspace
from temperature import temperature
from slipnet import slipnet
import formulas