Merge branch 'develop'

This commit is contained in:
LSaldyt
2017-11-01 11:14:24 -07:00
19 changed files with 163466 additions and 41 deletions

View File

@ -22,7 +22,6 @@ def codelet(name):
return f
return wrap
# some methods common to the codelets
def __showWhichStringObjectIsFrom(structure):
if not structure:

View File

@ -3,6 +3,10 @@ from .randomness import Randomness
from .slipnet import Slipnet
from .temperature import Temperature
from .workspace import Workspace
from .gui import GUI
from pprint import pprint
class Reporter(object):
"""Do-nothing base class for defining new reporter types"""
@ -23,30 +27,48 @@ class Reporter(object):
class Copycat(object):
def __init__(self, rng_seed=None, reporter=None):
def __init__(self, rng_seed=None, reporter=None, gui=False):
self.coderack = Coderack(self)
self.random = Randomness(rng_seed)
self.slipnet = Slipnet()
self.temperature = Temperature() # TODO: use entropy
self.workspace = Workspace(self)
self.reporter = reporter or Reporter()
if gui:
self.gui = GUI('Copycat')
self.lastUpdate = float('-inf')
def mainLoop(self, lastUpdate):
currentTime = self.coderack.codeletsRun
self.temperature.tryUnclamp(currentTime) # TODO: use entropy
# Every 15 codelets, we update the workspace.
if currentTime >= lastUpdate + 15:
self.workspace.updateEverything()
self.coderack.updateCodelets()
self.slipnet.update(self.random)
self.temperature.update(self.workspace.getUpdatedTemperature()) # TODO: use entropy
lastUpdate = currentTime
self.reporter.report_slipnet(self.slipnet)
def step(self):
self.coderack.chooseAndRunCodelet()
self.reporter.report_coderack(self.coderack)
self.reporter.report_temperature(self.temperature)
self.reporter.report_workspace(self.workspace)
return lastUpdate
def update_workspace(self, currentTime):
self.workspace.updateEverything()
self.coderack.updateCodelets()
self.slipnet.update(self.random)
self.temperature.update(self.workspace.getUpdatedTemperature())
self.lastUpdate = currentTime
self.reporter.report_slipnet(self.slipnet)
def check_reset(self):
if self.gui.app.primary.control.go:
initial, modified, target = self.gui.app.primary.control.get_vars()
self.gui.app.reset_with_strings(initial, modified, target)
self.workspace.resetWithStrings(initial, modified, target)
return True
else:
return False
def mainLoop(self):
currentTime = self.coderack.codeletsRun
self.temperature.tryUnclamp(currentTime) # TODO: use entropy
# Every 15 codelets, we update the workspace.
if currentTime >= self.lastUpdate + 15:
self.update_workspace(currentTime)
self.step()
def runTrial(self):
"""Run a trial of the copycat algorithm"""
@ -54,9 +76,8 @@ class Copycat(object):
self.slipnet.reset()
self.temperature.reset() # TODO: use entropy
self.workspace.reset()
lastUpdate = float('-inf')
while self.workspace.finalAnswer is None:
lastUpdate = self.mainLoop(lastUpdate)
self.mainLoop()
answer = {
'answer': self.workspace.finalAnswer,
'temp': self.temperature.last_unclamped_value, # TODO: use entropy
@ -65,34 +86,60 @@ class Copycat(object):
self.reporter.report_answer(answer)
return answer
def run(self, initial, modified, target, iterations):
self.workspace.resetWithStrings(initial, modified, target)
self.temperature.useAdj('original')
#self.temperature.useAdj('entropy')
#self.temperature.useAdj('inverse') # 100 weight
#self.temperature.useAdj('fifty_converge')
#self.temperature.useAdj('soft')
#self.temperature.useAdj('weighted_soft')
#self.temperature.useAdj('alt_fifty')
#self.temperature.useAdj('average_alt')
self.temperature.useAdj('best')
def runGUI(self):
while not self.check_reset():
self.gui.update(self)
self.gui.refresh()
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']
self.temperature.useAdj('pbest')
while True:
if self.check_reset():
answers = {}
self.gui.refresh()
if not self.gui.paused():
answer = self.runTrial()
self.gui.update(self)
d = answers.setdefault(answer['answer'], {
'count': 0,
'sumtemp': 0,
'sumtime': 0
})
d['count'] += 1
d['sumtemp'] += answer['temp']
d['sumtime'] += answer['time']
self.gui.add_answers(answers)
for answer, d in answers.items():
d['avgtemp'] = d.pop('sumtemp') / d['count']
d['avgtime'] = d.pop('sumtime') / d['count']
pprint(answers)
return answers
def run(self, initial, modified, target, iterations):
self.temperature.useAdj('best')
self.gui.app.reset_with_strings(initial, modified, target)
self.workspace.resetWithStrings(initial, modified, target)
answers = {}
for formula in ['original', 'best', 'sbest', 'pbest']:
self.temperature.useAdj(formula)
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']
print('The formula {} provided:'.format(formula))
pprint(answers)
return answers
def run_forever(self, initial, modified, target):

1
copycat/gui/__init__.py Normal file
View File

@ -0,0 +1 @@
from .gui import GUI

56
copycat/gui/control.py Normal file
View File

@ -0,0 +1,56 @@
import tkinter as tk
import tkinter.ttk as ttk
from .gridframe import GridFrame
from .entry import Entry
class Control(GridFrame):
def __init__(self, parent, *args, **kwargs):
GridFrame.__init__(self, parent, *args, **kwargs)
self.paused = True
self.steps = 0
self.go = False
self.playbutton = ttk.Button(self, text='Play', command=lambda : self.toggle())
self.add(self.playbutton, 0, 0)
self.stepbutton = ttk.Button(self, text='Step', command=lambda : self.step())
self.add(self.stepbutton, 1, 0)
self.entry = Entry(self)
self.add(self.entry, 0, 1, xspan=2)
self.gobutton = ttk.Button(self, text='Go', command=lambda : self.set_go())
self.add(self.gobutton, 0, 2, xspan=2)
def play(self):
self.paused = False
self.playbutton['text'] = 'Pause'
def pause(self):
self.paused = True
self.playbutton['text'] = 'Play'
def toggle(self):
if self.paused:
self.play()
else:
self.pause()
def step(self):
self.steps += 1
def has_step(self):
if self.steps > 0:
self.steps -= 1
return True
else:
return False
def set_go(self):
self.go = True
self.play()
def get_vars(self):
return self.entry.a.get(), self.entry.b.get(), self.entry.c.get()

27
copycat/gui/entry.py Normal file
View File

@ -0,0 +1,27 @@
import tkinter as tk
import tkinter.ttk as ttk
from .gridframe import GridFrame
class Entry(GridFrame):
def __init__(self, parent, *args, **kwargs):
GridFrame.__init__(self, parent, *args, **kwargs)
self.aLabel = ttk.Label(self, text='Initial:')
self.a = ttk.Entry(self, style='EntryStyle.TEntry')
self.add(self.aLabel, 0, 0)
self.add(self.a, 0, 1)
self.bLabel = ttk.Label(self, text='Final:')
self.b = ttk.Entry(self, style='EntryStyle.TEntry')
self.add(self.bLabel, 1, 0)
self.add(self.b, 1, 1)
self.cLabel = ttk.Label(self, text='Next:')
self.c = ttk.Entry(self, style='EntryStyle.TEntry')
self.add(self.cLabel, 2, 0)
self.add(self.c, 2, 1)
GridFrame.configure(self)

11
copycat/gui/gridframe.py Normal file
View File

@ -0,0 +1,11 @@
import tkinter as tk
import tkinter.ttk as ttk
class GridFrame(tk.Frame):
def __init__(self, parent, *args, **kwargs):
ttk.Frame.__init__(self, parent, *args, **kwargs)
def add(self, element, x, y, xspan=1, yspan=1):
element.grid(column=x, row=y, columnspan=xspan, rowspan=yspan, sticky=tk.N+tk.E+tk.S+tk.W)
tk.Grid.rowconfigure(self, x, weight=1)
tk.Grid.columnconfigure(self, y, weight=1)

99
copycat/gui/gui.py Normal file
View File

@ -0,0 +1,99 @@
import sys
import time
import tkinter as tk
import tkinter.ttk as ttk
from tkinter import scrolledtext
from tkinter import filedialog
import matplotlib.pyplot as plt
from .status import Status, StatusFrame
from .status import Plot
from .gridframe import GridFrame
from .primary import Primary
from .list import List
from .style import configure_style
from .plot import plot_imbedded
plt.style.use('dark_background')
class MainApplication(GridFrame):
def __init__(self, parent, *args, **kwargs):
GridFrame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.primary = Primary(self, *args, **kwargs)
self.add(self.primary, 0, 0, xspan=2)
self.create_widgets()
GridFrame.configure(self)
def create_widgets(self):
columns = 20
self.slipList = List(self, columns)
self.add(self.slipList, 0, 1)
self.codeletList = List(self, columns)
self.add(self.codeletList, 1, 1)
self.objectList = List(self, columns)
self.add(self.objectList, 2, 1)
self.graph2 = Plot(self, 'Answer Distribution')
self.add(self.graph2, 2, 0)
def update(self, copycat):
self.primary.update(copycat)
slipnodes = copycat.slipnet.slipnodes
codelets = copycat.coderack.codelets
objects = copycat.workspace.objects
self.slipList.update(slipnodes, key=lambda s:s.activation,
formatter=lambda s : '{}: {}'.format(s.name, round(s.activation, 2)))
self.codeletList.update(codelets, key=lambda c:c.urgency, formatter= lambda s : '{}: {}'.format(s.name, round(s.urgency, 2)))
get_descriptors = lambda s : ', '.join('({}={})'.format(d.descriptionType.name, d.descriptor.name) for d in s.descriptions)
self.objectList.update(objects, formatter=lambda s : '{}: {}'.format(s, get_descriptors(s)))
'''
if len(objects) > 0:
print('Descriptions:')
for obj in objects:
print(obj)
for description in obj.descriptions:
print(' {}:'.format(description))
print(' {}'.format(description.descriptionType.name))
print(' {}'.format(description.descriptor.name))
'''
def reset_with_strings(self, initial, modified, target):
self.primary.reset_with_strings(initial, modified, target)
class GUI(object):
def __init__(self, title):
self.root = tk.Tk()
self.root.title(title)
tk.Grid.rowconfigure(self.root, 0, weight=1)
tk.Grid.columnconfigure(self.root, 0, weight=1)
self.app = MainApplication(self.root)
self.app.grid(row=0, column=0, sticky=tk.N+tk.S+tk.E+tk.W)
configure_style(ttk.Style())
def add_answers(self, answers):
def modifier(status):
with plt.style.context(('dark_background')):
plot_imbedded(answers, status)
self.app.graph2.status.modifier = modifier
def refresh(self):
self.root.update_idletasks()
self.root.update()
def paused(self):
return self.app.primary.control.paused
def update(self, copycat):
self.app.update(copycat)

26
copycat/gui/list.py Normal file
View File

@ -0,0 +1,26 @@
import tkinter as tk
import tkinter.ttk as ttk
import time
from .gridframe import GridFrame
class List(GridFrame):
def __init__(self, parent, columns, updateInterval=.1):
GridFrame.__init__(self, parent)
self.text = ttk.Label(self, anchor='w', justify=tk.LEFT, width=30)
self.add(self.text, 0, 0)
self.columns = columns
self.lastUpdated = time.time()
self.updateInterval = updateInterval
def update(self, l, key=None, reverse=False, formatter=lambda s : str(s)):
current = time.time()
if current - self.lastUpdated > self.updateInterval:
l = l[:self.columns]
if key is not None:
l = sorted(l, key=key, reverse=False)
self.text['text'] = '\n'.join(map(formatter, l))

19
copycat/gui/plot.py Normal file
View File

@ -0,0 +1,19 @@
import matplotlib.pyplot as plt; plt.rcdefaults()
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('dark_background')
def plot_imbedded(answers, status):
answers = sorted(answers.items(), key=lambda kv : kv[1]['count'])
objects = [t[0] for t in answers]
yvalues = [t[1]['count'] for t in answers]
y_pos = np.arange(len(objects))
status.subplot.clear()
status.subplot.bar(y_pos, yvalues, align='center', alpha=0.5)
status.subplot.set_xticks(y_pos)
status.subplot.set_xticklabels(tuple(objects))
status.subplot.set_ylabel('Count')
status.subplot.set_title('Answers')

29
copycat/gui/primary.py Normal file
View File

@ -0,0 +1,29 @@
import tkinter as tk
import tkinter.ttk as ttk
from tkinter import scrolledtext
from tkinter import filedialog
from .control import Control
from .gridframe import GridFrame
from .workspacecanvas import WorkspaceCanvas
class Primary(GridFrame):
def __init__(self, parent, *args, **kwargs):
GridFrame.__init__(self, parent, *args, **kwargs)
self.canvas = WorkspaceCanvas(self)
self.add(self.canvas, 0, 0, xspan=2)
self.control = Control(self)
self.add(self.control, 0, 2)
GridFrame.configure(self)
def update(self, copycat):
self.canvas.update(copycat)
def reset_with_strings(self, initial, modified, target):
self.canvas.reset_with_strings(initial, modified, target)

66
copycat/gui/status.py Normal file
View File

@ -0,0 +1,66 @@
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import tkinter as tk
import tkinter.ttk as ttk
import time
import matplotlib.animation as animation
import matplotlib.pyplot as plt
plt.style.use('dark_background')
from .gridframe import GridFrame
class Plot(GridFrame):
def __init__(self, parent, title):
GridFrame.__init__(self, parent)
self.status = Status()
self.sframe = StatusFrame(self, self.status, title)
self.add(self.sframe, 0, 0, xspan=2)
self.savebutton = ttk.Button(self, text='Save to path:', command=lambda : self.save())
self.add(self.savebutton, 0, 1)
self.pathentry = ttk.Entry(self, style='EntryStyle.TEntry', textvariable='output/dist.png')
self.add(self.pathentry, 1, 1)
def save(self):
path = self.pathentry.get()
if len(path) > 0:
try:
self.status.figure.savefig(path)
except Exception as e:
print(e)
class StatusFrame(ttk.Frame):
def __init__(self, parent, status, title):
ttk.Frame.__init__(self, parent)
self.status = status
self.canvas = FigureCanvasTkAgg(status.figure, self)
self.canvas.show()
self.canvas.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)
self.animation = animation.FuncAnimation(status.figure, lambda i : status.update_plots(i), interval=1000)
class Status(object):
def __init__(self):
self.figure = Figure(figsize=(5,5), dpi=100)
self.subplot = self.figure.add_subplot(111)
self.x = []
self.y = []
def modifier(status):
with plt.style.context(('dark_background')):
status.subplot.plot(status.x, status.y)
self.modifier = modifier
self.update_plots(0)
def update_plots(self, i):
self.subplot.clear()
self.modifier(self)

33
copycat/gui/style.py Normal file
View File

@ -0,0 +1,33 @@
style_dict = dict(foreground='white',
background='black')
map_options = dict(
foreground=[('disabled', 'black'),
('pressed', 'white'),
('active', 'white')],
background=[('disabled', 'black'),
('pressed', '!focus', 'black'),
('active', 'black')],
highlightcolor=[('focus', 'black'),
('!focus', 'black')])
def configure_style(style):
style.configure('TButton', **style_dict)
style.map('TButton', **map_options)
style.configure('TLabel', **style_dict)
#style.configure('TEntry', **style_dict)
#style.map('TEntry', **map_options)
# A hack to change entry style
style.element_create("plain.field", "from", "clam")
style.layout("EntryStyle.TEntry",
[('Entry.plain.field', {'children': [(
'Entry.background', {'children': [(
'Entry.padding', {'children': [(
'Entry.textarea', {'sticky': 'nswe'})],
'sticky': 'nswe'})], 'sticky': 'nswe'})],
'border':'2', 'sticky': 'nswe'})])
style.configure("EntryStyle.TEntry",
background="black",
foreground="white",
fieldbackground="black")

159787
copycat/gui/sys Normal file

File diff suppressed because it is too large Load Diff

3093
copycat/gui/time Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,69 @@
import tkinter as tk
import tkinter.ttk as ttk
from .gridframe import GridFrame
font1Size = 32
font1 = ('Helvetica', font1Size)
class WorkspaceCanvas(GridFrame):
def __init__(self, parent, *args, **kwargs):
GridFrame.__init__(self, parent, *args, **kwargs)
self.chars = []
self.initial = ''
self.modified = ''
self.target = ''
self.answer = ''
self.changed = False
self.canvas = tk.Canvas(self, background='black')
self.add(self.canvas, 0, 0)
GridFrame.configure(self)
def update(self, copycat):
answer = '' if copycat.workspace.rule is None else copycat.workspace.rule.buildTranslatedRule()
if answer != self.answer:
self.changed = True
if self.changed:
self.canvas.delete('all')
del self.chars[:]
self.add_text()
def add_text(self):
padding = 100
def add_sequences(sequences, x, y):
for sequence in sequences:
x += padding
if sequence is None:
sequence = ''
for char in sequence:
self.chars.append((char, (x, y)))
self.canvas.create_text(x, y, text=char, anchor=tk.NW, font=font1, fill='white')
x += font1Size
return x, y
x = 0
y = padding
add_sequences([self.initial, self.modified], x, y)
x = 0
y += padding
add_sequences([self.target, self.answer], x, y)
def reset_with_strings(self, initial, modified, target):
if initial != self.initial or \
modified != self.modified or \
target != self.target:
self.changed = True
self.initial = initial
self.modified = modified
self.target = target

4
copycat/sampleText.txt Normal file
View File

@ -0,0 +1,4 @@
1,2
3,4
7,7
100,100

View File

@ -24,7 +24,7 @@ def _entropy(temp, prob):
f = (c + 1) * prob
return -f * math.log2(f)
def _weighted(temp, prob, s, u):
def _weighted(temp, prob, s, u, alpha=1, beta=1):
weighted = (temp / 100) * s + ((100 - temp) / 100) * u
return weighted
@ -56,12 +56,31 @@ def _averaged_alt(temp, prob):
u = prob ** 2 if prob < .5 else math.sqrt(prob)
return _weighted(temp, prob, s, u)
def _working_best(temp, prob):
s = .5 # convergence
r = 2 # power
u = prob ** r if prob < .5 else prob ** (1/r)
return _weighted(temp, prob, s, u)
def _soft_best(temp, prob):
s = .5 # convergence
r = 1.05 # power
u = prob ** r if prob < .5 else prob ** (1/r)
return _weighted(temp, prob, s, u)
def _parameterized_best(temp, prob):
# (D$66/100)*($E$64*$B68 + $G$64*$F$64)/($E$64 + $G$64)+((100-D$66)/100)*IF($B68 > 0.5, $B68^(1/$H$64), $B68^$H$64)
# (T/100) * (alpha * p + beta * .5) / (alpha + beta) + ((100 - T)/100) * IF(p > .5, p^(1/2), p^2)
alpha = 5
beta = 1
s = .5
s = (alpha * prob + beta * s) / (alpha + beta)
r = 1.05
u = prob ** r if prob < .5 else prob ** (1/r)
return _weighted(temp, prob, s, u)
class Temperature(object):
def __init__(self):
self.reset()
@ -75,7 +94,9 @@ class Temperature(object):
'weighted_soft' : _weighted_soft_curve,
'alt_fifty' : _alt_fifty,
'average_alt' : _averaged_alt,
'best' : _working_best}
'best' : _working_best,
'sbest' : _soft_best,
'pbest' : _parameterized_best}
def reset(self):
self.actual_value = 100.0

View File

@ -23,6 +23,16 @@ class Workspace(object):
self.intraStringUnhappiness = 0.0
self.interStringUnhappiness = 0.0
# LSaldyt: default initializations for GUI entry
self.targetString = ''
self.initialString = ''
self.modifiedString = ''
self.finalAnswer = None
self.changedObject = None
self.objects = []
self.structures = []
self.rule = None
def __repr__(self):
return '<Workspace trying %s:%s::%s:?>' % (
self.initialString, self.modifiedString, self.targetString,

28
gui.py Executable file
View File

@ -0,0 +1,28 @@
#!/usr/bin/env python3
import argparse
import logging
from copycat import Copycat, Reporter
import matplotlib.pyplot as plt
plt.style.use('dark_background')
class SimpleReporter(Reporter):
def report_answer(self, answer):
print('Answered %s (time %d, final temperature %.1f)' % (
answer['answer'], answer['time'], answer['temp'],
))
def main():
logging.basicConfig(level=logging.INFO, format='%(message)s', filename='./output/copycat.log', filemode='w')
parser = argparse.ArgumentParser()
parser.add_argument('--seed', type=int, default=None, help='Provide a deterministic seed for the RNG.')
options = parser.parse_args()
copycat = Copycat(reporter=SimpleReporter(), rng_seed=options.seed, gui=True)
copycat.runGUI()
if __name__ == '__main__':
main()