Major overhaul of "randomness" throughout.

- Nobody does `import random` anymore.

- Random numbers are gotten from `ctx.random`, which is an object
of type `Randomness` with all the convenience methods that used to
be obnoxious functions in the `formulas` module.

- Every place that was using `random.random()` to implement the
equivalent of Python3 `random.choices(seq, weights)` has been updated
to use `ctx.random.weighted_choice(seq, weights)`.

This has a functional effect, since the details of random number
generation have changed. The *statistical* effect should be small.
I do observe that Copycat is having trouble inventing the "mrrjjjj"
solution right now (even in 1000 test runs), so maybe something is
slightly broken.
This commit is contained in:
Arthur O'Dwyer
2017-04-17 22:18:37 -07:00
parent 8fdb9d06e6
commit 3732ae8475
11 changed files with 179 additions and 214 deletions

View File

@ -1,6 +1,5 @@
import inspect import inspect
import logging import logging
import random
import formulas import formulas
from workspaceFormulas import chooseDirectedNeighbor from workspaceFormulas import chooseDirectedNeighbor
@ -37,7 +36,9 @@ def __showWhichStringObjectIsFrom(structure):
#print 'object chosen = %s from %s string' % (structure, whence) #print 'object chosen = %s from %s string' % (structure, whence)
def __getScoutSource(workspace, slipnode, relevanceMethod, typeName): def __getScoutSource(ctx, slipnode, relevanceMethod, typeName):
random = ctx.random
workspace = ctx.workspace
initialRelevance = relevanceMethod(workspace.initial, slipnode) initialRelevance = relevanceMethod(workspace.initial, slipnode)
targetRelevance = relevanceMethod(workspace.target, slipnode) targetRelevance = relevanceMethod(workspace.target, slipnode)
initialUnhappiness = workspace.initial.intraStringUnhappiness initialUnhappiness = workspace.initial.intraStringUnhappiness
@ -47,11 +48,9 @@ def __getScoutSource(workspace, slipnode, relevanceMethod, typeName):
logging.info('target : relevance = %d, unhappiness=%d', logging.info('target : relevance = %d, unhappiness=%d',
targetRelevance, int(targetUnhappiness)) targetRelevance, int(targetUnhappiness))
string = workspace.initial string = workspace.initial
relevances = initialRelevance + targetRelevance
unhappinesses = initialUnhappiness + targetUnhappiness
randomized = random.random() * (relevances + unhappinesses)
initials = initialRelevance + initialUnhappiness initials = initialRelevance + initialUnhappiness
if randomized > initials: targets = targetRelevance + targetUnhappiness
if random.weighted_greater_than(targets, initials):
string = workspace.target string = workspace.target
logging.info('target string selected: %s for %s', logging.info('target string selected: %s for %s',
workspace.target, typeName) workspace.target, typeName)
@ -76,7 +75,9 @@ def __getDescriptors(bondFacet, source, destination):
def __structureVsStructure(structure1, weight1, structure2, weight2): def __structureVsStructure(structure1, weight1, structure2, weight2):
"""Return true if the first structure comes out stronger than the second."""
ctx = structure1.ctx ctx = structure1.ctx
random = ctx.random
temperature = ctx.temperature temperature = ctx.temperature
structure1.updateStrength() structure1.updateStrength()
structure2.updateStrength() structure2.updateStrength()
@ -84,9 +85,7 @@ def __structureVsStructure(structure1, weight1, structure2, weight2):
structure1.totalStrength * weight1) structure1.totalStrength * weight1)
weightedStrength2 = temperature.getAdjustedValue( weightedStrength2 = temperature.getAdjustedValue(
structure2.totalStrength * weight2) structure2.totalStrength * weight2)
rhs = (weightedStrength1 + weightedStrength2) * random.random() return random.weighted_greater_than(weightedStrength1, weightedStrength2)
logging.info('%d > %d', weightedStrength1, rhs)
return weightedStrength1 > rhs
def __fight(structure, structureWeight, incompatibles, incompatibleWeight): def __fight(structure, structureWeight, incompatibles, incompatibleWeight):
@ -115,21 +114,23 @@ def __fightIncompatibles(incompatibles, structure, name,
def __slippability(ctx, conceptMappings): def __slippability(ctx, conceptMappings):
random = ctx.random
temperature = ctx.temperature temperature = ctx.temperature
for mapping in conceptMappings: for mapping in conceptMappings:
slippiness = mapping.slippability() / 100.0 slippiness = mapping.slippability() / 100.0
probabilityOfSlippage = temperature.getAdjustedProbability(slippiness) probabilityOfSlippage = temperature.getAdjustedProbability(slippiness)
if formulas.coinFlip(probabilityOfSlippage): if random.coinFlip(probabilityOfSlippage):
return True return True
return False return False
@codelet('breaker') @codelet('breaker')
def breaker(ctx, codelet): def breaker(ctx, codelet):
random = ctx.random
temperature = ctx.temperature temperature = ctx.temperature
workspace = ctx.workspace workspace = ctx.workspace
probabilityOfFizzle = (100.0 - temperature.value()) / 100.0 probabilityOfFizzle = (100.0 - temperature.value()) / 100.0
if formulas.coinFlip(probabilityOfFizzle): if random.coinFlip(probabilityOfFizzle):
return return
# choose a structure at random # choose a structure at random
structures = [s for s in workspace.structures if structures = [s for s in workspace.structures if
@ -146,18 +147,28 @@ def breaker(ctx, codelet):
for structure in breakObjects: for structure in breakObjects:
breakProbability = temperature.getAdjustedProbability( breakProbability = temperature.getAdjustedProbability(
structure.totalStrength / 100.0) structure.totalStrength / 100.0)
if formulas.coinFlip(breakProbability): if random.coinFlip(breakProbability):
return return
for structure in breakObjects: for structure in breakObjects:
structure.break_the_structure() structure.break_the_structure()
def similarPropertyLinks(slip_node, temperature): def chooseRelevantDescriptionByActivation(ctx, workspaceObject):
random = ctx.random
descriptions = workspaceObject.relevantDescriptions()
weights = [description.descriptor.activation
for description in descriptions]
return random.weighted_choice(descriptions, weights)
def similarPropertyLinks(ctx, slip_node):
random = ctx.random
temperature = ctx.temperature
result = [] result = []
for slip_link in slip_node.propertyLinks: for slip_link in slip_node.propertyLinks:
association = slip_link.degreeOfAssociation() / 100.0 association = slip_link.degreeOfAssociation() / 100.0
probability = temperature.getAdjustedProbability(association) probability = temperature.getAdjustedProbability(association)
if formulas.coinFlip(probability): if random.coinFlip(probability):
result += [slip_link] result += [slip_link]
return result return result
@ -165,19 +176,21 @@ def similarPropertyLinks(slip_node, temperature):
@codelet('bottom-up-description-scout') @codelet('bottom-up-description-scout')
def bottom_up_description_scout(ctx, codelet): def bottom_up_description_scout(ctx, codelet):
coderack = ctx.coderack coderack = ctx.coderack
temperature = ctx.temperature random = ctx.random
workspace = ctx.workspace workspace = ctx.workspace
chosenObject = chooseUnmodifiedObject('totalSalience', workspace.objects) chosenObject = chooseUnmodifiedObject('totalSalience', workspace.objects)
assert chosenObject assert chosenObject
__showWhichStringObjectIsFrom(chosenObject) __showWhichStringObjectIsFrom(chosenObject)
description = formulas.chooseRelevantDescriptionByActivation(chosenObject) # choose relevant description by activation
descriptions = chosenObject.relevantDescriptions()
weights = [d.descriptor.activation for d in descriptions]
description = random.weighted_choice(descriptions, weights)
assert description assert description
sliplinks = similarPropertyLinks(description.descriptor, temperature) sliplinks = similarPropertyLinks(ctx, description.descriptor)
assert sliplinks assert sliplinks
values = [sliplink.degreeOfAssociation() * sliplink.destination.activation weights = [sliplink.degreeOfAssociation() * sliplink.destination.activation
for sliplink in sliplinks] for sliplink in sliplinks]
i = formulas.selectListPosition(values) chosen = random.weighted_choice(sliplinks, weights)
chosen = sliplinks[i]
chosenProperty = chosen.destination chosenProperty = chosen.destination
coderack.proposeDescription(chosenObject, chosenProperty.category(), coderack.proposeDescription(chosenObject, chosenProperty.category(),
chosenProperty, codelet) chosenProperty, codelet)
@ -186,6 +199,7 @@ def bottom_up_description_scout(ctx, codelet):
@codelet('top-down-description-scout') @codelet('top-down-description-scout')
def top_down_description_scout(ctx, codelet): def top_down_description_scout(ctx, codelet):
coderack = ctx.coderack coderack = ctx.coderack
random = ctx.random
workspace = ctx.workspace workspace = ctx.workspace
descriptionType = codelet.arguments[0] descriptionType = codelet.arguments[0]
chosenObject = chooseUnmodifiedObject('totalSalience', workspace.objects) chosenObject = chooseUnmodifiedObject('totalSalience', workspace.objects)
@ -193,9 +207,8 @@ def top_down_description_scout(ctx, codelet):
__showWhichStringObjectIsFrom(chosenObject) __showWhichStringObjectIsFrom(chosenObject)
descriptions = chosenObject.getPossibleDescriptions(descriptionType) descriptions = chosenObject.getPossibleDescriptions(descriptionType)
assert descriptions and len(descriptions) assert descriptions and len(descriptions)
values = [n.activation for n in descriptions] weights = [n.activation for n in descriptions]
i = formulas.selectListPosition(values) chosenProperty = random.weighted_choice(descriptions, weights)
chosenProperty = descriptions[i]
coderack.proposeDescription(chosenObject, chosenProperty.category(), coderack.proposeDescription(chosenObject, chosenProperty.category(),
chosenProperty, codelet) chosenProperty, codelet)
@ -203,13 +216,14 @@ def top_down_description_scout(ctx, codelet):
@codelet('description-strength-tester') @codelet('description-strength-tester')
def description_strength_tester(ctx, codelet): def description_strength_tester(ctx, codelet):
coderack = ctx.coderack coderack = ctx.coderack
random = ctx.random
temperature = ctx.temperature temperature = ctx.temperature
description = codelet.arguments[0] description = codelet.arguments[0]
description.descriptor.buffer = 100.0 description.descriptor.buffer = 100.0
description.updateStrength() description.updateStrength()
strength = description.totalStrength strength = description.totalStrength
probability = temperature.getAdjustedProbability(strength / 100.0) probability = temperature.getAdjustedProbability(strength / 100.0)
assert formulas.coinFlip(probability) assert random.coinFlip(probability)
coderack.newCodelet('description-builder', codelet, strength) coderack.newCodelet('description-builder', codelet, strength)
@ -255,6 +269,7 @@ def bottom_up_bond_scout(ctx, codelet):
@codelet('rule-scout') @codelet('rule-scout')
def rule_scout(ctx, codelet): def rule_scout(ctx, codelet):
coderack = ctx.coderack coderack = ctx.coderack
random = ctx.random
slipnet = ctx.slipnet slipnet = ctx.slipnet
temperature = ctx.temperature temperature = ctx.temperature
workspace = ctx.workspace workspace = ctx.workspace
@ -292,15 +307,13 @@ def rule_scout(ctx, codelet):
newList += [node] newList += [node]
objectList = newList # surely this should be += objectList = newList # surely this should be +=
# "union of this and distinguishing descriptors" # "union of this and distinguishing descriptors"
assert objectList and len(objectList) assert objectList
# use conceptual depth to choose a description # use conceptual depth to choose a description
valueList = [] weights = [
for node in objectList: temperature.getAdjustedValue(node.conceptualDepth)
depth = node.conceptualDepth for node in objectList
value = temperature.getAdjustedValue(depth) ]
valueList += [value] descriptor = random.weighted_choice(objectList, weights)
i = formulas.selectListPosition(valueList)
descriptor = objectList[i]
# choose the relation (change the letmost object to "successor" or "d" # choose the relation (change the letmost object to "successor" or "d"
objectList = [] objectList = []
if changed.replacement.relation: if changed.replacement.relation:
@ -308,13 +321,11 @@ def rule_scout(ctx, codelet):
objectList += [changed.replacement.objectFromModified.getDescriptor( objectList += [changed.replacement.objectFromModified.getDescriptor(
slipnet.letterCategory)] slipnet.letterCategory)]
# use conceptual depth to choose a relation # use conceptual depth to choose a relation
valueList = [] weights = [
for node in objectList: temperature.getAdjustedValue(node.conceptualDepth)
depth = node.conceptualDepth for node in objectList
value = temperature.getAdjustedValue(depth) ]
valueList += [value] relation = random.weighted_choice(objectList, weights)
i = formulas.selectListPosition(valueList)
relation = objectList[i]
coderack.proposeRule(slipnet.letterCategory, descriptor, coderack.proposeRule(slipnet.letterCategory, descriptor,
slipnet.letter, relation, codelet) slipnet.letter, relation, codelet)
@ -322,16 +333,18 @@ def rule_scout(ctx, codelet):
@codelet('rule-strength-tester') @codelet('rule-strength-tester')
def rule_strength_tester(ctx, codelet): def rule_strength_tester(ctx, codelet):
coderack = ctx.coderack coderack = ctx.coderack
random = ctx.random
temperature = ctx.temperature temperature = ctx.temperature
rule = codelet.arguments[0] rule = codelet.arguments[0]
rule.updateStrength() rule.updateStrength()
probability = temperature.getAdjustedProbability(rule.totalStrength / 100.0) probability = temperature.getAdjustedProbability(rule.totalStrength / 100.0)
if formulas.coinFlip(probability): if random.coinFlip(probability):
coderack.newCodelet('rule-builder', codelet, rule.totalStrength, rule) coderack.newCodelet('rule-builder', codelet, rule.totalStrength, rule)
@codelet('replacement-finder') @codelet('replacement-finder')
def replacement_finder(ctx, codelet): def replacement_finder(ctx, codelet):
random = ctx.random
slipnet = ctx.slipnet slipnet = ctx.slipnet
workspace = ctx.workspace workspace = ctx.workspace
# choose random letter in initial string # choose random letter in initial string
@ -374,10 +387,9 @@ def replacement_finder(ctx, codelet):
def top_down_bond_scout__category(ctx, codelet): def top_down_bond_scout__category(ctx, codelet):
coderack = ctx.coderack coderack = ctx.coderack
slipnet = ctx.slipnet slipnet = ctx.slipnet
workspace = ctx.workspace
logging.info('top_down_bond_scout__category') logging.info('top_down_bond_scout__category')
category = codelet.arguments[0] category = codelet.arguments[0]
source = __getScoutSource(workspace, category, formulas.localBondCategoryRelevance, source = __getScoutSource(ctx, category, formulas.localBondCategoryRelevance,
'bond') 'bond')
destination = chooseNeighbor(source) destination = chooseNeighbor(source)
logging.info('source: %s, destination: %s', source, destination) logging.info('source: %s, destination: %s', source, destination)
@ -406,9 +418,8 @@ def top_down_bond_scout__category(ctx, codelet):
def top_down_bond_scout__direction(ctx, codelet): def top_down_bond_scout__direction(ctx, codelet):
coderack = ctx.coderack coderack = ctx.coderack
slipnet = ctx.slipnet slipnet = ctx.slipnet
workspace = ctx.workspace
direction = codelet.arguments[0] direction = codelet.arguments[0]
source = __getScoutSource(workspace, source = __getScoutSource(ctx,
direction, formulas.localDirectionCategoryRelevance, 'bond') direction, formulas.localDirectionCategoryRelevance, 'bond')
destination = chooseDirectedNeighbor(source, direction) destination = chooseDirectedNeighbor(source, direction)
assert destination assert destination
@ -427,6 +438,7 @@ def top_down_bond_scout__direction(ctx, codelet):
@codelet('bond-strength-tester') @codelet('bond-strength-tester')
def bond_strength_tester(ctx, codelet): def bond_strength_tester(ctx, codelet):
coderack = ctx.coderack coderack = ctx.coderack
random = ctx.random
temperature = ctx.temperature temperature = ctx.temperature
bond = codelet.arguments[0] bond = codelet.arguments[0]
__showWhichStringObjectIsFrom(bond) __showWhichStringObjectIsFrom(bond)
@ -434,7 +446,7 @@ def bond_strength_tester(ctx, codelet):
strength = bond.totalStrength strength = bond.totalStrength
probability = temperature.getAdjustedProbability(strength / 100.0) probability = temperature.getAdjustedProbability(strength / 100.0)
logging.info('bond strength = %d for %s', strength, bond) logging.info('bond strength = %d for %s', strength, bond)
assert formulas.coinFlip(probability) assert random.coinFlip(probability)
bond.facet.buffer = 100.0 bond.facet.buffer = 100.0
bond.sourceDescriptor.buffer = 100.0 bond.sourceDescriptor.buffer = 100.0
bond.destinationDescriptor.buffer = 100.0 bond.destinationDescriptor.buffer = 100.0
@ -489,12 +501,12 @@ def bond_builder(ctx, codelet):
@codelet('top-down-group-scout--category') @codelet('top-down-group-scout--category')
def top_down_group_scout__category(ctx, codelet): def top_down_group_scout__category(ctx, codelet):
coderack = ctx.coderack coderack = ctx.coderack
random = ctx.random
slipnet = ctx.slipnet slipnet = ctx.slipnet
workspace = ctx.workspace
groupCategory = codelet.arguments[0] groupCategory = codelet.arguments[0]
category = groupCategory.getRelatedNode(slipnet.bondCategory) category = groupCategory.getRelatedNode(slipnet.bondCategory)
assert category assert category
source = __getScoutSource(workspace, category, formulas.localBondCategoryRelevance, source = __getScoutSource(ctx, category, formulas.localBondCategoryRelevance,
'group') 'group')
assert source and not source.spansString() assert source and not source.spansString()
if source.leftmost: if source.leftmost:
@ -502,11 +514,10 @@ def top_down_group_scout__category(ctx, codelet):
elif source.rightmost: elif source.rightmost:
direction = slipnet.left direction = slipnet.left
else: else:
activations = [slipnet.left.activation, slipnet.right.activation] direction = random.weighted_choice(
if not formulas.selectListPosition(activations): [slipnet.left, slipnet.right],
direction = slipnet.left [slipnet.left.activation, slipnet.right.activation]
else: )
direction = slipnet.right
if direction == slipnet.left: if direction == slipnet.left:
firstBond = source.leftBond firstBond = source.leftBond
else: else:
@ -522,7 +533,7 @@ def top_down_group_scout__category(ctx, codelet):
group = Group(source.string, slipnet.samenessGroup, group = Group(source.string, slipnet.samenessGroup,
None, slipnet.letterCategory, [source], []) None, slipnet.letterCategory, [source], [])
probability = group.singleLetterGroupProbability() probability = group.singleLetterGroupProbability()
if formulas.coinFlip(probability): if random.coinFlip(probability):
coderack.proposeSingleLetterGroup(source, codelet) coderack.proposeSingleLetterGroup(source, codelet)
return return
direction = firstBond.directionCategory direction = firstBond.directionCategory
@ -574,10 +585,10 @@ def top_down_group_scout__category(ctx, codelet):
@codelet('top-down-group-scout--direction') @codelet('top-down-group-scout--direction')
def top_down_group_scout__direction(ctx, codelet): def top_down_group_scout__direction(ctx, codelet):
coderack = ctx.coderack coderack = ctx.coderack
random = ctx.random
slipnet = ctx.slipnet slipnet = ctx.slipnet
workspace = ctx.workspace
direction = codelet.arguments[0] direction = codelet.arguments[0]
source = __getScoutSource(workspace, direction, source = __getScoutSource(ctx, direction,
formulas.localDirectionCategoryRelevance, formulas.localDirectionCategoryRelevance,
'direction') 'direction')
logging.info('source chosen = %s', source) logging.info('source chosen = %s', source)
@ -587,12 +598,10 @@ def top_down_group_scout__direction(ctx, codelet):
elif source.rightmost: elif source.rightmost:
mydirection = slipnet.left mydirection = slipnet.left
else: else:
activations = [slipnet.left.activation] mydirection = random.weighted_choice(
activations += [slipnet.right.activation] [slipnet.left, slipnet.right],
if not formulas.selectListPosition(activations): [slipnet.left.activation, slipnet.right.activation]
mydirection = slipnet.left )
else:
mydirection = slipnet.right
if mydirection == slipnet.left: if mydirection == slipnet.left:
firstBond = source.leftBond firstBond = source.leftBond
else: else:
@ -669,9 +678,10 @@ def top_down_group_scout__direction(ctx, codelet):
@codelet('group-scout--whole-string') @codelet('group-scout--whole-string')
def group_scout__whole_string(ctx, codelet): def group_scout__whole_string(ctx, codelet):
coderack = ctx.coderack coderack = ctx.coderack
random = ctx.random
slipnet = ctx.slipnet slipnet = ctx.slipnet
workspace = ctx.workspace workspace = ctx.workspace
if formulas.coinFlip(): if random.coinFlip():
string = workspace.target string = workspace.target
logging.info('target string selected: %s', workspace.target) logging.info('target string selected: %s', workspace.target)
else: else:
@ -713,6 +723,7 @@ def group_scout__whole_string(ctx, codelet):
@codelet('group-strength-tester') @codelet('group-strength-tester')
def group_strength_tester(ctx, codelet): def group_strength_tester(ctx, codelet):
coderack = ctx.coderack coderack = ctx.coderack
random = ctx.random
slipnet = ctx.slipnet slipnet = ctx.slipnet
temperature = ctx.temperature temperature = ctx.temperature
# update strength value of the group # update strength value of the group
@ -721,7 +732,7 @@ def group_strength_tester(ctx, codelet):
group.updateStrength() group.updateStrength()
strength = group.totalStrength strength = group.totalStrength
probability = temperature.getAdjustedProbability(strength / 100.0) probability = temperature.getAdjustedProbability(strength / 100.0)
if formulas.coinFlip(probability): if random.coinFlip(probability):
# it is strong enough - post builder & activate nodes # it is strong enough - post builder & activate nodes
group.groupCategory.getRelatedNode(slipnet.bondCategory).buffer = 100.0 group.groupCategory.getRelatedNode(slipnet.bondCategory).buffer = 100.0
if group.directionCategory: if group.directionCategory:
@ -821,29 +832,23 @@ def rule_builder(ctx, codelet):
workspace.buildRule(rule) workspace.buildRule(rule)
def __getCutOff(density): def __getCutoffWeights(bondDensity):
if density > 0.8: if bondDensity > 0.8:
distribution = [5.0, 150.0, 5.0, 2.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] return [5.0, 150.0, 5.0, 2.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
elif density > 0.6: elif bondDensity > 0.6:
distribution = [2.0, 5.0, 150.0, 5.0, 2.0, 1.0, 1.0, 1.0, 1.0, 1.0] return [2.0, 5.0, 150.0, 5.0, 2.0, 1.0, 1.0, 1.0, 1.0, 1.0]
elif density > 0.4: elif bondDensity > 0.4:
distribution = [1.0, 2.0, 5.0, 150.0, 5.0, 2.0, 1.0, 1.0, 1.0, 1.0] return [1.0, 2.0, 5.0, 150.0, 5.0, 2.0, 1.0, 1.0, 1.0, 1.0]
elif density > 0.2: elif bondDensity > 0.2:
distribution = [1.0, 1.0, 2.0, 5.0, 150.0, 5.0, 2.0, 1.0, 1.0, 1.0] return [1.0, 1.0, 2.0, 5.0, 150.0, 5.0, 2.0, 1.0, 1.0, 1.0]
else: else:
distribution = [1.0, 1.0, 1.0, 2.0, 5.0, 150.0, 5.0, 2.0, 1.0, 1.0] return [1.0, 1.0, 1.0, 2.0, 5.0, 150.0, 5.0, 2.0, 1.0, 1.0]
stop = sum(distribution) * random.random()
total = 0.0
for i in xrange(len(distribution)):
total += distribution[i]
if total >= stop:
return i + 1
return len(distribution)
@codelet('rule-translator') @codelet('rule-translator')
def rule_translator(ctx, codelet): def rule_translator(ctx, codelet):
coderack = ctx.coderack coderack = ctx.coderack
random = ctx.random
temperature = ctx.temperature temperature = ctx.temperature
workspace = ctx.workspace workspace = ctx.workspace
assert workspace.rule assert workspace.rule
@ -854,9 +859,9 @@ def rule_translator(ctx, codelet):
len(workspace.target.bonds)) len(workspace.target.bonds))
nearlyTotalLength = len(workspace.initial) + len(workspace.target) - 2 nearlyTotalLength = len(workspace.initial) + len(workspace.target) - 2
bondDensity = numberOfBonds / nearlyTotalLength bondDensity = numberOfBonds / nearlyTotalLength
if bondDensity > 1.0: bondDensity = min(bondDensity, 1.0)
bondDensity = 1.0 weights = __getCutoffWeights(bondDensity)
cutoff = __getCutOff(bondDensity) * 10.0 cutoff = 10.0 * random.weighted_choice(range(1, 11), weights)
if cutoff >= temperature.actual_value: if cutoff >= temperature.actual_value:
if workspace.rule.buildTranslatedRule(): if workspace.rule.buildTranslatedRule():
workspace.foundAnswer = True workspace.foundAnswer = True
@ -905,24 +910,19 @@ def bottom_up_correspondence_scout(ctx, codelet):
conceptMappings, flipTargetObject, codelet) conceptMappings, flipTargetObject, codelet)
def chooseSlipnodeByConceptualDepth(slip_nodes, temperature):
if not slip_nodes:
return None
depths = [temperature.getAdjustedValue(n.conceptualDepth) for n in slip_nodes]
i = formulas.selectListPosition(depths)
return slip_nodes[i]
@codelet('important-object-correspondence-scout') @codelet('important-object-correspondence-scout')
def important_object_correspondence_scout(ctx, codelet): def important_object_correspondence_scout(ctx, codelet):
coderack = ctx.coderack coderack = ctx.coderack
random = ctx.random
slipnet = ctx.slipnet slipnet = ctx.slipnet
temperature = ctx.temperature temperature = ctx.temperature
workspace = ctx.workspace workspace = ctx.workspace
objectFromInitial = chooseUnmodifiedObject('relativeImportance', objectFromInitial = chooseUnmodifiedObject('relativeImportance',
workspace.initial.objects) workspace.initial.objects)
descriptors = objectFromInitial.relevantDistinguishingDescriptors() descriptors = objectFromInitial.relevantDistinguishingDescriptors()
slipnode = chooseSlipnodeByConceptualDepth(descriptors, temperature) # choose descriptor by conceptual depth
weights = [temperature.getAdjustedValue(n.conceptualDepth) for n in descriptors]
slipnode = random.weighted_choice(descriptors, weights)
assert slipnode assert slipnode
initialDescriptor = slipnode initialDescriptor = slipnode
for mapping in workspace.slippages(): for mapping in workspace.slippages():
@ -971,6 +971,7 @@ def important_object_correspondence_scout(ctx, codelet):
@codelet('correspondence-strength-tester') @codelet('correspondence-strength-tester')
def correspondence_strength_tester(ctx, codelet): def correspondence_strength_tester(ctx, codelet):
coderack = ctx.coderack coderack = ctx.coderack
random = ctx.random
temperature = ctx.temperature temperature = ctx.temperature
workspace = ctx.workspace workspace = ctx.workspace
correspondence = codelet.arguments[0] correspondence = codelet.arguments[0]
@ -984,7 +985,7 @@ def correspondence_strength_tester(ctx, codelet):
correspondence.updateStrength() correspondence.updateStrength()
strength = correspondence.totalStrength strength = correspondence.totalStrength
probability = temperature.getAdjustedProbability(strength / 100.0) probability = temperature.getAdjustedProbability(strength / 100.0)
if formulas.coinFlip(probability): if random.coinFlip(probability):
# activate some concepts # activate some concepts
for mapping in correspondence.conceptMappings: for mapping in correspondence.conceptMappings:
mapping.initialDescriptionType.buffer = 100.0 mapping.initialDescriptionType.buffer = 100.0

View File

@ -1,9 +1,7 @@
import math import math
import logging import logging
import random
import codeletMethods import codeletMethods
import formulas
from bond import Bond from bond import Bond
from codelet import Codelet from codelet import Codelet
from correspondence import Correspondence from correspondence import Correspondence
@ -95,6 +93,7 @@ class Coderack(object):
return result return result
def howManyToPost(self, codeletName): def howManyToPost(self, codeletName):
random = self.ctx.random
workspace = self.ctx.workspace workspace = self.ctx.workspace
if codeletName == 'breaker' or 'description' in codeletName: if codeletName == 'breaker' or 'description' in codeletName:
return 1 return 1
@ -117,9 +116,9 @@ class Coderack(object):
number = workspace.numberOfUnreplacedObjects() number = workspace.numberOfUnreplacedObjects()
if 'correspondence' in codeletName: if 'correspondence' in codeletName:
number = workspace.numberOfUncorrespondingObjects() number = workspace.numberOfUncorrespondingObjects()
if number < formulas.blur(2.0): if number < random.sqrtBlur(2.0):
return 1 return 1
if number < formulas.blur(4.0): if number < random.sqrtBlur(4.0):
return 2 return 2
return 3 return 3
@ -131,6 +130,7 @@ class Coderack(object):
self.removeCodelet(oldCodelet) self.removeCodelet(oldCodelet)
def postTopDownCodelets(self): def postTopDownCodelets(self):
random = self.ctx.random
slipnet = self.ctx.slipnet slipnet = self.ctx.slipnet
for node in slipnet.slipnodes: for node in slipnet.slipnodes:
#logging.info('Trying slipnode: %s' % node.get_name()) #logging.info('Trying slipnode: %s' % node.get_name())
@ -141,7 +141,7 @@ class Coderack(object):
probability = self.probabilityOfPosting(codeletName) probability = self.probabilityOfPosting(codeletName)
howMany = self.howManyToPost(codeletName) howMany = self.howManyToPost(codeletName)
for _ in xrange(howMany): for _ in xrange(howMany):
if not formulas.coinFlip(probability): if not random.coinFlip(probability):
continue continue
urgency = getUrgencyBin( urgency = getUrgencyBin(
node.activation * node.conceptualDepth / 100.0) node.activation * node.conceptualDepth / 100.0)
@ -164,6 +164,7 @@ class Coderack(object):
self.__postBottomUpCodelets('breaker') self.__postBottomUpCodelets('breaker')
def __postBottomUpCodelets(self, codeletName): def __postBottomUpCodelets(self, codeletName):
random = self.ctx.random
temperature = self.ctx.temperature temperature = self.ctx.temperature
probability = self.probabilityOfPosting(codeletName) probability = self.probabilityOfPosting(codeletName)
howMany = self.howManyToPost(codeletName) howMany = self.howManyToPost(codeletName)
@ -173,7 +174,7 @@ class Coderack(object):
if temperature.value() < 25.0 and 'translator' in codeletName: if temperature.value() < 25.0 and 'translator' in codeletName:
urgency = 5 urgency = 5
for _ in xrange(howMany): for _ in xrange(howMany):
if formulas.coinFlip(probability): if random.coinFlip(probability):
codelet = Codelet(codeletName, urgency, self.codeletsRun) codelet = Codelet(codeletName, urgency, self.codeletsRun)
self.post(codelet) self.post(codelet)
@ -264,20 +265,13 @@ class Coderack(object):
def chooseOldCodelet(self): def chooseOldCodelet(self):
# selects an old codelet to remove from the coderack # selects an old codelet to remove from the coderack
# more likely to select lower urgency codelets # more likely to select lower urgency codelets
if not len(self.codelets):
return None
urgencies = [] urgencies = []
for codelet in self.codelets: for codelet in self.codelets:
urgency = ((self.codeletsRun - codelet.birthdate) * urgency = ((self.codeletsRun - codelet.birthdate) *
(7.5 - codelet.urgency)) (7.5 - codelet.urgency))
urgencies += [urgency] urgencies += [urgency]
threshold = random.random() * sum(urgencies) random = self.ctx.random
sumOfUrgencies = 0.0 return random.weighted_choice(self.codelets, urgencies)
for i in xrange(len(self.codelets)):
sumOfUrgencies += urgencies[i]
if sumOfUrgencies > threshold:
return self.codelets[i]
return self.codelets[0]
def postInitialCodelets(self): def postInitialCodelets(self):
workspace = self.ctx.workspace workspace = self.ctx.workspace
@ -300,22 +294,12 @@ class Coderack(object):
self.run(codelet) self.run(codelet)
def chooseCodeletToRun(self): def chooseCodeletToRun(self):
random = self.ctx.random
temperature = self.ctx.temperature temperature = self.ctx.temperature
assert self.codelets assert self.codelets
scale = (100.0 - temperature.value() + 10.0) / 15.0 scale = (100.0 - temperature.value() + 10.0) / 15.0
urgsum = sum(codelet.urgency ** scale for codelet in self.codelets) chosen = random.weighted_choice(self.codelets, [codelet.urgency ** scale for codelet in self.codelets])
threshold = random.random() * urgsum
chosen = self.codelets[0]
urgencySum = 0.0
for codelet in self.codelets:
urgencySum += codelet.urgency ** scale
if urgencySum > threshold:
chosen = codelet
break
self.removeCodelet(chosen) self.removeCodelet(chosen)
logging.info('chosen codelet\n\t%s, urgency = %s',
chosen.name, chosen.urgency)
return chosen return chosen
def run(self, codelet): def run(self, codelet):

View File

@ -3,10 +3,11 @@ import logging
class Context(object): class Context(object):
def __init__(self): def __init__(self):
self.temperature = None
self.coderack = None self.coderack = None
self.workspace = None self.random = None
self.slipnet = None self.slipnet = None
self.temperature = None
self.workspace = None
def mainLoop(self, lastUpdate): def mainLoop(self, lastUpdate):
currentTime = self.coderack.codeletsRun currentTime = self.coderack.codeletsRun
@ -15,7 +16,7 @@ class Context(object):
if currentTime >= lastUpdate + 15: if currentTime >= lastUpdate + 15:
self.workspace.updateEverything() self.workspace.updateEverything()
self.coderack.updateCodelets() self.coderack.updateCodelets()
self.slipnet.update() self.slipnet.update(self.random)
self.temperature.update(self.workspace.getUpdatedTemperature()) self.temperature.update(self.workspace.getUpdatedTemperature())
lastUpdate = currentTime lastUpdate = currentTime
logging.debug('Number of codelets: %d', len(self.coderack.codelets)) logging.debug('Number of codelets: %d', len(self.coderack.codelets))

View File

@ -1,16 +1,16 @@
import logging from coderack import Coderack
from randomness import Randomness
from workspace import Workspace
from slipnet import Slipnet from slipnet import Slipnet
from temperature import Temperature from temperature import Temperature
from coderack import Coderack from workspace import Workspace
from context import context from context import context
context.coderack = Coderack(context)
context.random = Randomness(42)
context.slipnet = Slipnet() context.slipnet = Slipnet()
context.temperature = Temperature() context.temperature = Temperature()
context.coderack = Coderack(context)
context.workspace = Workspace(context) context.workspace = Workspace(context)

View File

@ -1,25 +1,6 @@
import math
import random
from conceptMapping import ConceptMapping from conceptMapping import ConceptMapping
def selectListPosition(probabilities):
total = sum(probabilities)
#logging.info('total: %s' % total)
r = random.random()
stopPosition = total * r
#logging.info('stopPosition: %s' % stopPosition)
total = 0
i = 0
for probability in probabilities:
total += probability
if total > stopPosition:
return i
i += 1
return 0
def weightedAverage(values): def weightedAverage(values):
total = 0.0 total = 0.0
totalWeights = 0.0 totalWeights = 0.0
@ -31,27 +12,6 @@ def weightedAverage(values):
return total / totalWeights return total / totalWeights
def coinFlip(chance=0.5):
return random.random() < chance
def blur(value):
root = math.sqrt(value)
if coinFlip():
return value + root
return value - root
def chooseRelevantDescriptionByActivation(workspaceObject):
descriptions = workspaceObject.relevantDescriptions()
if not descriptions:
return None
activations = [description.descriptor.activation
for description in descriptions]
i = selectListPosition(activations)
return descriptions[i]
def __relevantCategory(objekt, slipnode): def __relevantCategory(objekt, slipnode):
return objekt.rightBond and objekt.rightBond.category == slipnode return objekt.rightBond and objekt.rightBond.category == slipnode

View File

@ -1,5 +1,4 @@
import logging import logging
import random
from workspaceObject import WorkspaceObject from workspaceObject import WorkspaceObject
import formulas import formulas
@ -73,9 +72,10 @@ class Group(WorkspaceObject):
def add_length_description_category(self): def add_length_description_category(self):
#check whether or not to add length description category #check whether or not to add length description category
random = self.ctx.random
slipnet = self.ctx.slipnet slipnet = self.ctx.slipnet
probability = self.lengthDescriptionProbability() probability = self.lengthDescriptionProbability()
if random.random() < probability: if random.coinFlip(probability):
length = len(self.objectList) length = len(self.objectList)
if length < 6: if length < 6:
self.addDescription(slipnet.length, self.addDescription(slipnet.length,

View File

@ -1,11 +1,8 @@
"""Run the copycat program""" """Run the copycat program"""
import logging import logging
import random
import sys import sys
random.seed(42)
import copycat import copycat

41
copycat/randomness.py Normal file
View File

@ -0,0 +1,41 @@
import bisect
import math
import random
def accumulate(iterable):
total = 0
for v in iterable:
total += v
yield total
class Randomness(object):
def __init__(self, seed=None):
self.rng = random.Random(seed)
def coinFlip(self, p=0.5):
return self.rng.random() < p
def choice(self, seq):
return self.rng.choice(seq)
def weighted_choice(self, seq, weights):
if not seq:
# Many callers rely on this behavior.
return None
else:
cum_weights = list(accumulate(weights))
total = cum_weights[-1]
return seq[bisect.bisect_left(cum_weights, self.rng.random() * total)]
def weighted_greater_than(self, first, second):
total = first + second
if total == 0:
return False
return self.coinFlip(float(first) / total)
def sqrtBlur(self, value):
# This is exceedingly dumb, but it matches the Java code.
root = math.sqrt(value)
if self.coinFlip():
return value + root
return value - root

View File

@ -32,7 +32,7 @@ class Slipnet(object):
for node in self.initiallyClampedSlipnodes: for node in self.initiallyClampedSlipnodes:
node.clampHigh() node.clampHigh()
def update(self): def update(self, random):
logging.debug('slipnet.update()') logging.debug('slipnet.update()')
self.numberOfUpdates += 1 self.numberOfUpdates += 1
if self.numberOfUpdates == 50: if self.numberOfUpdates == 50:
@ -44,7 +44,7 @@ class Slipnet(object):
node.spread_activation() node.spread_activation()
for node in self.slipnodes: for node in self.slipnodes:
node.addBuffer() node.addBuffer()
node.jump() node.jump(random)
node.buffer = 0.0 node.buffer = 0.0
def isDistinguishingDescriptor(self, descriptor): def isDistinguishingDescriptor(self, descriptor):

View File

@ -1,10 +1,5 @@
import math import math
import logging import logging
import random
def full_activation():
return 100
def jump_threshold(): def jump_threshold():
@ -65,11 +60,7 @@ class Slipnode(object):
def fully_active(self): def fully_active(self):
"""Whether this node has full activation""" """Whether this node has full activation"""
float_margin = 0.00001 float_margin = 0.00001
return self.activation > full_activation() - float_margin return self.activation > 100.0 - float_margin
def activate_fully(self):
"""Make this node fully active"""
self.activation = full_activation()
def bondDegreeOfAssociation(self): def bondDegreeOfAssociation(self):
linkLength = self.intrinsicLinkLength linkLength = self.intrinsicLinkLength
@ -112,7 +103,8 @@ class Slipnode(object):
If no linked node is found, return None If no linked node is found, return None
""" """
if relation == self.slipnet.identity: slipnet = self.slipnet
if relation == slipnet.identity:
return self return self
destinations = [l.destination destinations = [l.destination
for l in self.outgoingLinks if l.label == relation] for l in self.outgoingLinks if l.label == relation]
@ -125,9 +117,10 @@ class Slipnode(object):
If it does not exist return None If it does not exist return None
""" """
slipnet = self.slipnet
result = None result = None
if self == destination: if self == destination:
result = self.slipnet.identity result = slipnet.identity
else: else:
for link in self.outgoingLinks: for link in self.outgoingLinks:
if link.destination == destination: if link.destination == destination:
@ -149,17 +142,14 @@ class Slipnode(object):
self.activation += self.buffer self.activation += self.buffer
self.activation = min(max(0, self.activation), 100) self.activation = min(max(0, self.activation), 100)
def can_jump(self): def jump(self, random):
if self.activation <= jump_threshold(): if self.activation <= jump_threshold():
return False return
if self.clamped: if self.clamped:
return False return
value = (self.activation / 100.0) ** 3 value = (self.activation / 100.0) ** 3
return random.random() < value if random.coinFlip(value):
self.activation = 100.0
def jump(self):
if self.can_jump():
self.activate_fully()
def get_name(self): def get_name(self):
if len(self.name) == 1: if len(self.name) == 1:

View File

@ -1,34 +1,29 @@
import logging import logging
import formulas
def __chooseObjectFromList(ctx, objects, attribute):
def __chooseObjectFromList(temperature, objects, attribute): random = ctx.random
if not objects: temperature = ctx.temperature
return None
weights = [ weights = [
temperature.getAdjustedValue( temperature.getAdjustedValue(
getattr(o, attribute) getattr(o, attribute)
) )
for o in objects for o in objects
] ]
i = formulas.selectListPosition(weights) return random.weighted_choice(objects, weights)
return objects[i]
def chooseUnmodifiedObject(attribute, inObjects): def chooseUnmodifiedObject(attribute, inObjects):
from context import context as ctx from context import context as ctx
temperature = ctx.temperature
workspace = ctx.workspace workspace = ctx.workspace
objects = [o for o in inObjects if o.string != workspace.modified] objects = [o for o in inObjects if o.string != workspace.modified]
if not len(objects): if not len(objects):
print 'no objects available in initial or target strings' print 'no objects available in initial or target strings'
return __chooseObjectFromList(temperature, objects, attribute) return __chooseObjectFromList(ctx, objects, attribute)
def chooseNeighbor(source): def chooseNeighbor(source):
from context import context as ctx from context import context as ctx
temperature = ctx.temperature
workspace = ctx.workspace workspace = ctx.workspace
objects = [] objects = []
for objekt in workspace.objects: for objekt in workspace.objects:
@ -38,7 +33,7 @@ def chooseNeighbor(source):
objects += [objekt] objects += [objekt]
elif source.leftIndex == objekt.rightIndex + 1: elif source.leftIndex == objekt.rightIndex + 1:
objects += [objekt] objects += [objekt]
return __chooseObjectFromList(temperature, objects, "intraStringSalience") return __chooseObjectFromList(ctx, objects, "intraStringSalience")
def chooseDirectedNeighbor(source, direction): def chooseDirectedNeighbor(source, direction):
@ -53,7 +48,6 @@ def chooseDirectedNeighbor(source, direction):
def __chooseLeftNeighbor(source): def __chooseLeftNeighbor(source):
from context import context as ctx from context import context as ctx
temperature = ctx.temperature
workspace = ctx.workspace workspace = ctx.workspace
objects = [] objects = []
for o in workspace.objects: for o in workspace.objects:
@ -64,32 +58,29 @@ def __chooseLeftNeighbor(source):
else: else:
logging.info('%s is not on left of %s', o, source) logging.info('%s is not on left of %s', o, source)
logging.info('Number of left objects: %s', len(objects)) logging.info('Number of left objects: %s', len(objects))
return __chooseObjectFromList(temperature, objects, 'intraStringSalience') return __chooseObjectFromList(ctx, objects, 'intraStringSalience')
def __chooseRightNeighbor(source): def __chooseRightNeighbor(source):
from context import context as ctx from context import context as ctx
temperature = ctx.temperature
workspace = ctx.workspace workspace = ctx.workspace
objects = [o for o in workspace.objects objects = [o for o in workspace.objects
if o.string == source.string if o.string == source.string
and o.leftIndex == source.rightIndex + 1] and o.leftIndex == source.rightIndex + 1]
return __chooseObjectFromList(temperature, objects, 'intraStringSalience') return __chooseObjectFromList(ctx, objects, 'intraStringSalience')
def chooseBondFacet(source, destination): def chooseBondFacet(source, destination):
from context import context as ctx from context import context as ctx
random = ctx.random
slipnet = ctx.slipnet slipnet = ctx.slipnet
sourceFacets = [d.descriptionType for d in source.descriptions sourceFacets = [d.descriptionType for d in source.descriptions
if d.descriptionType in slipnet.bondFacets] if d.descriptionType in slipnet.bondFacets]
bondFacets = [d.descriptionType for d in destination.descriptions bondFacets = [d.descriptionType for d in destination.descriptions
if d.descriptionType in sourceFacets] if d.descriptionType in sourceFacets]
if not bondFacets:
return None
supports = [__supportForDescriptionType(f, source.string) supports = [__supportForDescriptionType(f, source.string)
for f in bondFacets] for f in bondFacets]
i = formulas.selectListPosition(supports) return random.weighted_choice(bondFacets, supports)
return bondFacets[i]
def __supportForDescriptionType(descriptionType, string): def __supportForDescriptionType(descriptionType, string):