From 17826b3470191f43a32b1e9fc05ea9ea9df5eba5 Mon Sep 17 00:00:00 2001 From: Isaac Karth Date: Sun, 18 Feb 2018 11:51:41 -0800 Subject: [PATCH 01/11] better logging --- model.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/model.py b/model.py index 94f784a..98704f4 100644 --- a/model.py +++ b/model.py @@ -13,6 +13,7 @@ import random import xml.etree.ElementTree as ET import collections +import logging try: import Image @@ -47,6 +48,8 @@ def __init__(self, width, height): self.observe_count = 0 self.count_prop_passes = 0 + + self.SAVE_IN_PROGRESS = False def Observe(self): self.observe_count += 1 @@ -141,7 +144,8 @@ def Run(self, seed, limit): while(presult): presult = self.Propagate() - self.Graphics().save("in_progress_{0}_{1}.png".format(hackstring, hackcount), format="PNG") + if self.SAVE_IN_PROGRESS: + self.Graphics().save("in_progress_{0}_{1}.png".format(hackstring, hackcount), format="PNG") hackcount += 1 #print("Propagate: {0}".format(pcount)) @@ -193,6 +197,7 @@ def __init__(self, width, height, name, N_value = 2, periodic_input_value = True self.patterns= [[]] #self.ground = 0 + def FuncPattern(passed_func): result = [0 for _ in range(self.N * self.N)] for y in range(0, self.N): @@ -292,7 +297,7 @@ def Agrees(p1, p2, dx, dy): for y in range(ymin, ymax): for x in range(xmin, xmax): if p1[x + self.N * y] != p2[x - dx + self.N * (y - dy)]: - print(p1[x + self.N * y] != p2[x - dx + self.N * (y - dy)]) + logging.debug(p1[x + self.N * y] != p2[x - dx + self.N * (y - dy)]) ifany = False #return False return ifany @@ -427,7 +432,7 @@ def Graphics(self): if contributors > 0: bitmap_data[x + y * self.FMX] = (int(r / contributors), int(g / contributors), int(b / contributors)) else: - print("WARNING: No contributors") + logging.info("INFO: No contributors") bitmap_data[x + y * self.FMX] = (int(r), int(g), int(b)) result.putdata(bitmap_data) return result From 26d5b68752da5bd846c0fac25742891a1b81b686 Mon Sep 17 00:00:00 2001 From: Isaac Karth Date: Sun, 18 Feb 2018 11:52:37 -0800 Subject: [PATCH 02/11] experimental performance improvement --- model.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/model.py b/model.py index 98704f4..c4c8fd9 100644 --- a/model.py +++ b/model.py @@ -377,7 +377,7 @@ def Propagate(self): return change - def Graphics(self): + def Graphics(self, monochrome=False): result = Image.new("RGB",(self.FMX, self.FMY),(0,0,0)) bitmap_data = list(result.getdata()) if(self.observed != None): @@ -393,8 +393,11 @@ def Graphics(self): local_patt = self.patterns[local_obsv][dx + dy * self.N] c = self.colors[local_patt] #bitmap_data[x + y * self.FMX] = (0xff000000 | (c.R << 16) | (c.G << 8) | c.B) - if isinstance(c, (int, float)): - bitmap_data[x + y * self.FMX] = (c, c, c) + if monochrome: + if isinstance(c, (int, float)): + bitmap_data[x + y * self.FMX] = (c, c, c) + else: + bitmap_data[x + y * self.FMX] = (c[0], c[1], c[2]) else: bitmap_data[x + y * self.FMX] = (c[0], c[1], c[2]) From 79ff21e5bfca01d80b19b585bcffed8d0ca09dd3 Mon Sep 17 00:00:00 2001 From: Isaac Karth Date: Sun, 18 Feb 2018 12:11:03 -0800 Subject: [PATCH 03/11] Added the ability to inject additional samples into the overlap model --- model.py | 94 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 38 deletions(-) diff --git a/model.py b/model.py index c4c8fd9..b10e8b4 100644 --- a/model.py +++ b/model.py @@ -172,24 +172,33 @@ def Graphics(self): class OverlappingModel(Model): - def __init__(self, width, height, name, N_value = 2, periodic_input_value = True, periodic_output_value = False, symmetry_value = 8, ground_value = 0): + def __init__(self, width, height, name, N_value = 2, periodic_input_value = True, periodic_output_value = False, symmetry_value = 8, ground_value = 0, additional_samples=[]): super( OverlappingModel, self).__init__(width, height) self.propagator = [[[[]]]] self.N = N_value self.periodic = periodic_output_value - self.bitmap = Image.open("samples/{0}.png".format(name)) - self.SMX = self.bitmap.size[0] - self.SMY = self.bitmap.size[1] - self.sample = [[0 for _ in range(self.SMY)] for _ in range(self.SMX)] + self.bitmaps = [Image.open("samples/{0}.png".format(name))] + self.SMXs = [self.bitmaps[0].size[0]] + self.SMYs = [self.bitmaps[0].size[1]] + self.samples = [[[0 for _ in range(self.SMYs[0])] for _ in range(self.SMXs[0])]] + + for additional in additional_samples: + self.bitmaps.append(Image.open("samples/{0}.png".format(additional))) + add_index = len(self.bitmaps)-1 + self.SMXs.append(self.bitmaps[add_index].size[0]) + self.SMYs.append(self.bitmaps[add_index].size[1]) + self.samples.append([[0 for _ in range(self.SMYs[add_index])] for _ in range(self.SMXs[add_index])]) + self.colors = [] - for y in range(0, self.SMY): - for x in range(0, self.SMX): - a_color = self.bitmap.getpixel((x, y)) - color_exists = [c for c in self.colors if c == a_color] - if len(color_exists) < 1: - self.colors.append(a_color) - samp_result = [i for i,v in enumerate(self.colors) if v == a_color] - self.sample[x][y] = samp_result + for samp_n in range(len(self.bitmaps)): + for y in range(0, self.SMYs[samp_n]): + for x in range(0, self.SMXs[samp_n]): + a_color = self.bitmaps[samp_n].getpixel((x, y)) + color_exists = [c for c in self.colors if c == a_color] + if len(color_exists) < 1: + self.colors.append(a_color) + samp_result = [i for i,v in enumerate(self.colors) if v == a_color] + self.samples[samp_n][x][y] = samp_result self.color_count = len(self.colors) self.W = StuffPower(self.color_count, self.N * self.N) @@ -197,6 +206,7 @@ def __init__(self, width, height, name, N_value = 2, periodic_input_value = True self.patterns= [[]] #self.ground = 0 + def FuncPattern(passed_func): result = [0 for _ in range(self.N * self.N)] @@ -207,9 +217,9 @@ def FuncPattern(passed_func): pattern_func = FuncPattern - def PatternFromSample(x, y): + def PatternFromSample(x, y, n=0): def innerPattern(dx, dy): - return self.sample[(x + dx) % self.SMX][(y + dy) % self.SMY] + return self.samples[n][(x + dx) % self.SMXs[n]][(y + dy) % self.SMYs[n]] return pattern_func(innerPattern) def Rotate(p): return FuncPattern(lambda x, y: p[self.N - 1 - y + x * self.N]) @@ -242,28 +252,29 @@ def PatternFromIndex(ind): self.weights = collections.Counter() ordering = [] - ylimit = self.SMY - self.N + 1 - xlimit = self.SMX - self.N + 1 - if True == periodic_input_value: - ylimit = self.SMY - xlimit = self.SMX - for y in range (0, ylimit): - for x in range(0, xlimit): - ps = [0 for _ in range(8)] - ps[0] = PatternFromSample(x,y) - ps[1] = Reflect(ps[0]) - ps[2] = Rotate(ps[0]) - ps[3] = Reflect(ps[2]) - ps[4] = Rotate(ps[2]) - ps[5] = Reflect(ps[4]) - ps[6] = Rotate(ps[4]) - ps[7] = Reflect(ps[6]) - for k in range(0,symmetry_value): - ind = Index(ps[k]) - indexed_weight = collections.Counter({ind : 1}) - self.weights = self.weights + indexed_weight - if not ind in ordering: - ordering.append(ind) + for samp_n in range(len(self.bitmaps)): + ylimit = self.SMYs[samp_n] - self.N + 1 + xlimit = self.SMXs[samp_n] - self.N + 1 + if True == periodic_input_value: + ylimit = self.SMYs[samp_n] + xlimit = self.SMXs[samp_n] + for y in range (0, ylimit): + for x in range(0, xlimit): + ps = [0 for _ in range(8)] + ps[0] = PatternFromSample(x,y,samp_n) + ps[1] = Reflect(ps[0]) + ps[2] = Rotate(ps[0]) + ps[3] = Reflect(ps[2]) + ps[4] = Rotate(ps[2]) + ps[5] = Reflect(ps[4]) + ps[6] = Rotate(ps[4]) + ps[7] = Reflect(ps[6]) + for k in range(0,symmetry_value): + ind = Index(ps[k]) + indexed_weight = collections.Counter({ind : 1}) + self.weights = self.weights + indexed_weight + if not ind in ordering: + ordering.append(ind) self.T = len(self.weights) self.ground = int((ground_value + self.T) % self.T) @@ -534,7 +545,14 @@ def Main(self): print("< {0} ".format(name), end='') if "overlapping" == xnode.tag: #print(xnode.attrib) - a_model = OverlappingModel(int(xnode.get('width', 48)), int(xnode.get('height', 48)), xnode.get('name', "NAME"), int(xnode.get('N', 2)), string2bool(xnode.get('periodicInput', True)), string2bool(xnode.get('periodic', False)), int(xnode.get('symmetry', 8)), int(xnode.get('ground',0))) + add_samp_string = xnode.get('additional', "NONE") + + add_samp = [] + if "NONE" != add_samp_string: + add_samp = add_samp_string.split(':') + + + a_model = OverlappingModel(int(xnode.get('width', 48)), int(xnode.get('height', 48)), xnode.get('name', "NAME"), int(xnode.get('N', 2)), string2bool(xnode.get('periodicInput', True)), string2bool(xnode.get('periodic', False)), int(xnode.get('symmetry', 8)), int(xnode.get('ground',0)), additional_samples=add_samp) pass elif "simpletiled" == xnode.tag: print("> ", end="\n") From 356832af78e2585859b01e0e8ee7d3c57958f253 Mon Sep 17 00:00:00 2001 From: Isaac Karth Date: Sun, 18 Feb 2018 12:56:41 -0800 Subject: [PATCH 04/11] can specify individual periodic attribute for each additional sample --- model.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/model.py b/model.py index b10e8b4..23d4bf7 100644 --- a/model.py +++ b/model.py @@ -14,6 +14,7 @@ import xml.etree.ElementTree as ET import collections import logging +import uuid try: import Image @@ -172,7 +173,7 @@ def Graphics(self): class OverlappingModel(Model): - def __init__(self, width, height, name, N_value = 2, periodic_input_value = True, periodic_output_value = False, symmetry_value = 8, ground_value = 0, additional_samples=[]): + def __init__(self, width, height, name, N_value = 2, periodic_input_value = True, periodic_output_value = False, symmetry_value = 8, ground_value = 0, additional_samples=[], additional_periodic=[]): super( OverlappingModel, self).__init__(width, height) self.propagator = [[[[]]]] self.N = N_value @@ -189,6 +190,9 @@ def __init__(self, width, height, name, N_value = 2, periodic_input_value = True self.SMYs.append(self.bitmaps[add_index].size[1]) self.samples.append([[0 for _ in range(self.SMYs[add_index])] for _ in range(self.SMXs[add_index])]) + + + self.colors = [] for samp_n in range(len(self.bitmaps)): for y in range(0, self.SMYs[samp_n]): @@ -206,6 +210,12 @@ def __init__(self, width, height, name, N_value = 2, periodic_input_value = True self.patterns= [[]] #self.ground = 0 + # Additional samples can individually be marked as periodic/non-periodic + periodic_input_values = [periodic_input_value] + for _ in range(len(self.bitmaps)): + periodic_input_values.append(periodic_input_value) + for idx_peri, peri in enumerate(additional_periodic): + periodic_input_values[idx_peri + 1] = ((1 == peri) or ('T' == peri) or ('True' == peri)) def FuncPattern(passed_func): @@ -255,7 +265,7 @@ def PatternFromIndex(ind): for samp_n in range(len(self.bitmaps)): ylimit = self.SMYs[samp_n] - self.N + 1 xlimit = self.SMXs[samp_n] - self.N + 1 - if True == periodic_input_value: + if True == periodic_input_values[samp_n]: ylimit = self.SMYs[samp_n] xlimit = self.SMXs[samp_n] for y in range (0, ylimit): @@ -550,9 +560,14 @@ def Main(self): add_samp = [] if "NONE" != add_samp_string: add_samp = add_samp_string.split(':') + + add_peri_string = xnode.get('additional_periodic', "NONE") + add_peri = [] + if "NONE" != add_peri_string: + add_samp = add_peri_string.split(':') - a_model = OverlappingModel(int(xnode.get('width', 48)), int(xnode.get('height', 48)), xnode.get('name', "NAME"), int(xnode.get('N', 2)), string2bool(xnode.get('periodicInput', True)), string2bool(xnode.get('periodic', False)), int(xnode.get('symmetry', 8)), int(xnode.get('ground',0)), additional_samples=add_samp) + a_model = OverlappingModel(int(xnode.get('width', 48)), int(xnode.get('height', 48)), xnode.get('name', "NAME"), int(xnode.get('N', 2)), string2bool(xnode.get('periodicInput', True)), string2bool(xnode.get('periodic', False)), int(xnode.get('symmetry', 8)), int(xnode.get('ground',0)), additional_samples=add_samp, additional_periodic=add_peri) pass elif "simpletiled" == xnode.tag: print("> ", end="\n") @@ -569,7 +584,7 @@ def Main(self): finished = a_model.Run(seed, int(xnode.get("limit", 0))) if finished: print("DONE") - a_model.Graphics().save("{0} {1} {2}.png".format(counter, name, i), format="PNG") + a_model.Graphics().save("{0}_{1}_{2}_{3}.png".format(counter, name, i, uuid.uuid4()), format="PNG") break else: print("CONTRADICTION") From c02c31040198c4c636e6d2e04bb6272f38664946 Mon Sep 17 00:00:00 2001 From: Isaac Karth Date: Sun, 25 Feb 2018 10:00:35 -0800 Subject: [PATCH 05/11] testing --- .gitignore | 2 ++ samples.xml | 77 +++-------------------------------------------------- 2 files changed, 5 insertions(+), 74 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..570f03f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +oldimages/ +.spyproject/ diff --git a/samples.xml b/samples.xml index c280657..a53028d 100644 --- a/samples.xml +++ b/samples.xml @@ -1,77 +1,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + From e7bc31f366494bd8889cb5a8bef797174cabe357 Mon Sep 17 00:00:00 2001 From: Isaac Karth Date: Sun, 25 Feb 2018 20:13:27 -0800 Subject: [PATCH 06/11] testing adjacency blacklist creation --- model.py | 16 ++++++++++++++-- samples/Yellow Maze.png | Bin 0 -> 2807 bytes 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 samples/Yellow Maze.png diff --git a/model.py b/model.py index 23d4bf7..45f04b8 100644 --- a/model.py +++ b/model.py @@ -325,18 +325,30 @@ def Agrees(p1, p2, dx, dy): #return True for x in range(0, 2 * self.N - 1): + print('x',x) self.propagator[x] = [[[0]] for _ in range(2 * self.N - 1)] for y in range(0, 2 * self.N - 1): + print('y',y) self.propagator[x][y] = [[0] for _ in range(self.T)] for t in range(0, self.T): + print('t',t) a_list = [] + print('pattern',self.patterns[t]) for t2 in range(0, self.T): - if Agrees(self.patterns[t], self.patterns[t2], x - self.N + 1, y - self.N + 1): - a_list.append(t2) + if not (((2 in self.patterns[t]) and (3 in self.patterns[t2])) or ((3 in self.patterns[t]) and (2 in self.patterns[t2]))): + if Agrees(self.patterns[t], self.patterns[t2], x - self.N + 1, y - self.N + 1): + a_list.append(t2) self.propagator[x][y][t] = [0 for _ in range(len(a_list))] for c in range(0, len(a_list)): self.propagator[x][y][t][c] = a_list[c] + + for x in self.propagator: + for y in x: + for t in y: + print('-----') + for c in t: + print(c) return def OnBoundary(self, x, y): diff --git a/samples/Yellow Maze.png b/samples/Yellow Maze.png new file mode 100644 index 0000000000000000000000000000000000000000..f7e47991edd7dd82936335870d2254fcab76153d GIT binary patch literal 2807 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 Date: Sun, 25 Feb 2018 22:53:46 -0800 Subject: [PATCH 07/11] automatically learn antipatterns from supplied images --- model.py | 116 +++++++++++++++++++++++++-------- samples.xml | 5 +- samples/test_antipattern.png | Bin 0 -> 2809 bytes samples/test_antipattern10.png | Bin 0 -> 2809 bytes samples/test_antipattern2.png | Bin 0 -> 2803 bytes samples/test_antipattern3.png | Bin 0 -> 2798 bytes samples/test_antipattern4.png | Bin 0 -> 2803 bytes samples/test_antipattern5.png | Bin 0 -> 2810 bytes samples/test_antipattern6.png | Bin 0 -> 2816 bytes samples/test_antipattern7.png | Bin 0 -> 2815 bytes samples/test_antipattern8.png | Bin 0 -> 2816 bytes samples/test_antipattern9.png | Bin 0 -> 2809 bytes 12 files changed, 91 insertions(+), 30 deletions(-) create mode 100644 samples/test_antipattern.png create mode 100644 samples/test_antipattern10.png create mode 100644 samples/test_antipattern2.png create mode 100644 samples/test_antipattern3.png create mode 100644 samples/test_antipattern4.png create mode 100644 samples/test_antipattern5.png create mode 100644 samples/test_antipattern6.png create mode 100644 samples/test_antipattern7.png create mode 100644 samples/test_antipattern8.png create mode 100644 samples/test_antipattern9.png diff --git a/model.py b/model.py index 1b8d912..ee1af74 100644 --- a/model.py +++ b/model.py @@ -173,7 +173,7 @@ def Graphics(self): class OverlappingModel(Model): - def __init__(self, width, height, name, N_value = 2, periodic_input_value = True, periodic_output_value = False, symmetry_value = 8, ground_value = 0): + def __init__(self, width, height, name, N_value = 2, periodic_input_value = True, periodic_output_value = False, symmetry_value = 8, ground_value = 0, additional_samples=[], additional_periodic="", antipatterns=""): """ Initializes the model. """ @@ -181,20 +181,39 @@ def __init__(self, width, height, name, N_value = 2, periodic_input_value = True self.propagator = [[[[]]]] self.N = N_value self.periodic = periodic_output_value - self.bitmaps = [Image.open("samples/{0}.png".format(name))] + self.bitmaps = [Image.open("samples/{0}.png".format(name)).convert("RGBA")] self.SMXs = [self.bitmaps[0].size[0]] self.SMYs = [self.bitmaps[0].size[1]] self.samples = [[[0 for _ in range(self.SMYs[0])] for _ in range(self.SMXs[0])]] + self.antipattern_flags = [int(x) for x in list(antipatterns.ljust(len(additional_samples) + 1, '0'))] + print(self.antipattern_flags) + + add_periodic = 0 + if periodic_input_value: + add_periodic = 1 + self.periodic_flags = [add_periodic] + [int(x) for x in list(additional_periodic.ljust(len(additional_samples), str(add_periodic)))] + print(self.periodic_flags) + for additional in additional_samples: - self.bitmaps.append(Image.open("samples/{0}.png".format(additional))) + self.bitmaps.append(Image.open("samples/{0}.png".format(additional)).convert("RGBA")) add_index = len(self.bitmaps)-1 self.SMXs.append(self.bitmaps[add_index].size[0]) self.SMYs.append(self.bitmaps[add_index].size[1]) self.samples.append([[0 for _ in range(self.SMYs[add_index])] for _ in range(self.SMXs[add_index])]) - - +# self.antibitmaps = [] +# self.aSMXs = [] +# self.aSMYs = [] +# self.antisamples = [] +# for anti in antipatterns: +# self.antibitmaps.append(Image.open("samples/{0}.png".format(anti))) +# add_index = len(self.antibitmaps)-1 +# self.SMXs.append(self.antibitmaps[add_index].size[0]) +# self.SMYs.append(self.antibitmaps[add_index].size[1]) +# self.antisamples.append([[0 for _ in range(self.aSMYs[add_index])] for _ in range(self.aSMXs[add_index])]) + + self.colors = [] for samp_n in range(len(self.bitmaps)): @@ -207,6 +226,8 @@ def __init__(self, width, height, name, N_value = 2, periodic_input_value = True samp_result = [i for i,v in enumerate(self.colors) if v == a_color] self.samples[samp_n][x][y] = samp_result + for c in self.colors: + print(c) self.color_count = len(self.colors) self.W = StuffPower(self.color_count, self.N * self.N) @@ -216,11 +237,11 @@ def __init__(self, width, height, name, N_value = 2, periodic_input_value = True #self.ground = 0 # Additional samples can individually be marked as periodic/non-periodic - periodic_input_values = [periodic_input_value] - for _ in range(len(self.bitmaps)): - periodic_input_values.append(periodic_input_value) - for idx_peri, peri in enumerate(additional_periodic): - periodic_input_values[idx_peri + 1] = ((1 == peri) or ('T' == peri) or ('True' == peri)) + #periodic_input_values = [periodic_input_value] + #for _ in range(len(self.bitmaps)): + # periodic_input_values.append(periodic_input_value) + #for idx_peri, peri in enumerate(additional_periodic): + # periodic_input_values[idx_peri + 1] = ((1 == peri) or ('T' == peri) or ('True' == peri)) def FuncPattern(passed_func): @@ -263,7 +284,7 @@ def Index(p): def PatternFromIndex(ind): ''' - Takes a pattern index and returns the pattern byte power index. + Takes a pattern index and returns the pattern byte array. ''' residue = ind power = self.W @@ -279,11 +300,13 @@ def PatternFromIndex(ind): self.weights = collections.Counter() ordering = [] + antiordering = [] + self.anti_adjacency = [] for samp_n in range(len(self.bitmaps)): ylimit = self.SMYs[samp_n] - self.N + 1 xlimit = self.SMXs[samp_n] - self.N + 1 - if True == periodic_input_values[samp_n]: + if 1 == self.periodic_flags[samp_n]: ylimit = self.SMYs[samp_n] xlimit = self.SMXs[samp_n] for y in range (0, ylimit): @@ -299,10 +322,14 @@ def PatternFromIndex(ind): ps[7] = Reflect(ps[6]) for k in range(0,symmetry_value): ind = Index(ps[k]) + if 1 == self.antipattern_flags[samp_n]: + if not ind in antiordering: + antiordering.append(ind) indexed_weight = collections.Counter({ind : 1}) self.weights = self.weights + indexed_weight if not ind in ordering: ordering.append(ind) + self.T = len(self.weights) self.ground = int((ground_value + self.T) % self.T) @@ -311,16 +338,55 @@ def PatternFromIndex(ind): self.stationary = [None for _ in range(self.T)] self.propagator = [[[[0]]] for _ in range(2 * self.N - 1)] + self.antipatterns = [[None] for _ in antiordering] + + for w in antiordering: + self.antipatterns.append(PatternFromIndex(w)) + counter = 0 for w in ordering: self.patterns[counter] = PatternFromIndex(w) self.stationary[counter] = self.weights[w] counter += 1 + for samp_n in range(len(self.bitmaps)): + if 1 == self.antipattern_flags[samp_n]: + #print('sample #',samp_n) + ylimit = self.SMYs[samp_n] - self.N + 1 + xlimit = self.SMXs[samp_n] - self.N + 1 + if 1 == self.periodic_flags[samp_n]: + ylimit = self.SMYs[samp_n] + xlimit = self.SMXs[samp_n] + #print('limits',xlimit, ylimit, self.periodic_flags[samp_n]) + for py in range (0, ylimit): + for px in range(0, xlimit): + pattern_one = PatternFromIndex(Index(PatternFromSample(px,py,samp_n))) + #print('pattern_one', pattern_one) + for tx in range(1 - self.N, self.N): + for ty in range(1 - self.N,self.N): + #print(tx,ty) + if not (tx == 0 and ty == 0): + if (1 == self.periodic_flags[samp_n]) or ((px + tx >= 0) and (py + ty >= 0) and (px + tx < xlimit) and (py + ty < ylimit)): + pattern_two = PatternFromIndex(Index(PatternFromSample(px+tx,py+ty,samp_n))) + #print('pattern_two', pattern_two, px + tx, py + ty, tx, ty) + self.anti_adjacency.append((pattern_one, pattern_two, (tx, ty))) + print('create antipattern', (pattern_one, pattern_two, (tx, ty))) + for x in range(0, self.FMX): for y in range(0, self.FMY): self.wave[x][y] = [False for _ in range(self.T)] + + def Disallowed(p1, p2, dx, dy): + #print(p1,p2,'\n---\n') + for anti_adj in self.anti_adjacency: + #print(anti_adj) + if (anti_adj[0] == p1 and anti_adj[1] == p2): + if dx == anti_adj[2][0] and dy == anti_adj[2][1]: + #print('antipattern:',p1,p2,dx,dy,'\n',anti_adj,'\n') + return True + return False + def Agrees(p1, p2, dx, dy): ifany = True xmin = dx @@ -343,18 +409,19 @@ def Agrees(p1, p2, dx, dy): #return True for x in range(0, 2 * self.N - 1): - print('x',x) + #print('x',x) self.propagator[x] = [[[0]] for _ in range(2 * self.N - 1)] for y in range(0, 2 * self.N - 1): - print('y',y) + #print('y',y) self.propagator[x][y] = [[0] for _ in range(self.T)] for t in range(0, self.T): - print('t',t) + #print('t',t) a_list = [] - print('pattern',self.patterns[t]) + #print('pattern',self.patterns[t]) for t2 in range(0, self.T): - if not (((2 in self.patterns[t]) and (3 in self.patterns[t2])) or ((3 in self.patterns[t]) and (2 in self.patterns[t2]))): + #if not (((2 in self.patterns[t]) and (3 in self.patterns[t2])) or ((3 in self.patterns[t]) and (2 in self.patterns[t2]))): + if not Disallowed(self.patterns[t], self.patterns[t2], x - self.N + 1, y - self.N + 1): if Agrees(self.patterns[t], self.patterns[t2], x - self.N + 1, y - self.N + 1): a_list.append(t2) self.propagator[x][y][t] = [0 for _ in range(len(a_list))] @@ -364,9 +431,10 @@ def Agrees(p1, p2, dx, dy): for x in self.propagator: for y in x: for t in y: - print('-----') + #print('-----') for c in t: - print(c) + #print(c) + pass return def OnBoundary(self, x, y): @@ -591,13 +659,9 @@ def Main(self): if "NONE" != add_samp_string: add_samp = add_samp_string.split(':') - add_peri_string = xnode.get('additional_periodic', "NONE") - add_peri = [] - if "NONE" != add_peri_string: - add_samp = add_peri_string.split(':') - - - a_model = OverlappingModel(int(xnode.get('width', 48)), int(xnode.get('height', 48)), xnode.get('name', "NAME"), int(xnode.get('N', 2)), string2bool(xnode.get('periodicInput', True)), string2bool(xnode.get('periodic', False)), int(xnode.get('symmetry', 8)), int(xnode.get('ground',0)), additional_samples=add_samp, additional_periodic=add_peri) + add_peri = xnode.get('additional_periodic', "") + + a_model = OverlappingModel(int(xnode.get('width', 48)), int(xnode.get('height', 48)), xnode.get('name', "NAME"), int(xnode.get('N', 2)), string2bool(xnode.get('periodicInput', True)), string2bool(xnode.get('periodic', False)), int(xnode.get('symmetry', 8)), int(xnode.get('ground',0)), additional_samples=add_samp, additional_periodic=add_peri, antipatterns=xnode.get('antipatterns', '0')) pass elif "simpletiled" == xnode.tag: print("> ", end="\n") diff --git a/samples.xml b/samples.xml index a53028d..6a63575 100644 --- a/samples.xml +++ b/samples.xml @@ -1,6 +1,3 @@ - - - - + diff --git a/samples/test_antipattern.png b/samples/test_antipattern.png new file mode 100644 index 0000000000000000000000000000000000000000..4bf1c61e36785381865ee0da63bb457a5b079421 GIT binary patch literal 2809 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 LNkvXXu0mjf=9f05 literal 0 HcmV?d00001 diff --git a/samples/test_antipattern10.png b/samples/test_antipattern10.png new file mode 100644 index 0000000000000000000000000000000000000000..5d830abdb3920b09f02980417f3a16aa56afb81f GIT binary patch literal 2809 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 FV1f_#F@FF6 literal 0 HcmV?d00001 diff --git a/samples/test_antipattern3.png b/samples/test_antipattern3.png new file mode 100644 index 0000000000000000000000000000000000000000..264e43dd86131e89bdaa181921110c4fc553d4b8 GIT binary patch literal 2798 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 A3jhEB literal 0 HcmV?d00001 diff --git a/samples/test_antipattern4.png b/samples/test_antipattern4.png new file mode 100644 index 0000000000000000000000000000000000000000..1541aa4fb64f845a8fbcf55e12ec7c03ddf0b6a3 GIT binary patch literal 2803 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 FV1mq-G^79k literal 0 HcmV?d00001 diff --git a/samples/test_antipattern5.png b/samples/test_antipattern5.png new file mode 100644 index 0000000000000000000000000000000000000000..8d5ad1c3ad47be1b7ec41ffc18fec9a43cdf96a9 GIT binary patch literal 2810 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 Date: Tue, 27 Feb 2018 16:12:44 -0800 Subject: [PATCH 08/11] flowers with injection and antipatterns --- model.py | 27 ++++++++++++++++++--------- samples.xml | 3 ++- samples/anti_flower_ground.png | Bin 0 -> 2804 bytes samples/blue_flowers.png | Bin 0 -> 2821 bytes samples/blue_flowers2.png | Bin 0 -> 2825 bytes samples/blue_flowers3.png | Bin 0 -> 2844 bytes samples/blue_flowers4.png | Bin 0 -> 2825 bytes samples/blue_flowers5.png | Bin 0 -> 2842 bytes samples/blue_flowers6.png | Bin 0 -> 2852 bytes samples/blue_flowers8.psd | Bin 0 -> 23853 bytes samples/blue_flowers8_anti1.png | Bin 0 -> 2808 bytes samples/blue_flowers8_anti2.png | Bin 0 -> 2804 bytes samples/blue_flowers9.png | Bin 0 -> 2826 bytes samples/blue_flowers9_anti1.png | Bin 0 -> 2804 bytes samples/red_flowers.png | Bin 0 -> 2971 bytes 15 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 samples/anti_flower_ground.png create mode 100644 samples/blue_flowers.png create mode 100644 samples/blue_flowers2.png create mode 100644 samples/blue_flowers3.png create mode 100644 samples/blue_flowers4.png create mode 100644 samples/blue_flowers5.png create mode 100644 samples/blue_flowers6.png create mode 100644 samples/blue_flowers8.psd create mode 100644 samples/blue_flowers8_anti1.png create mode 100644 samples/blue_flowers8_anti2.png create mode 100644 samples/blue_flowers9.png create mode 100644 samples/blue_flowers9_anti1.png create mode 100644 samples/red_flowers.png diff --git a/model.py b/model.py index ee1af74..aa00799 100644 --- a/model.py +++ b/model.py @@ -23,6 +23,10 @@ hackstring = "" hackcount = 0 + +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + class Model: def __init__(self, width, height): @@ -322,6 +326,7 @@ def PatternFromIndex(ind): ps[7] = Reflect(ps[6]) for k in range(0,symmetry_value): ind = Index(ps[k]) + logger.info('pattern: ' + str(ps[k]) + ' index ' + str(ind)) if 1 == self.antipattern_flags[samp_n]: if not ind in antiordering: antiordering.append(ind) @@ -330,9 +335,11 @@ def PatternFromIndex(ind): if not ind in ordering: ordering.append(ind) - + for indo, o in enumerate(ordering): + print(indo, o, PatternFromIndex(o)) self.T = len(self.weights) - self.ground = int((ground_value + self.T) % self.T) + self.ground = (102,106)# int((ground_value + self.T) % self.T) + print('self.ground',self.ground) self.patterns = [[None] for _ in range(self.T)] self.stationary = [None for _ in range(self.T)] @@ -361,7 +368,7 @@ def PatternFromIndex(ind): for py in range (0, ylimit): for px in range(0, xlimit): pattern_one = PatternFromIndex(Index(PatternFromSample(px,py,samp_n))) - #print('pattern_one', pattern_one) + print('pattern_one', pattern_one) for tx in range(1 - self.N, self.N): for ty in range(1 - self.N,self.N): #print(tx,ty) @@ -370,7 +377,7 @@ def PatternFromIndex(ind): pattern_two = PatternFromIndex(Index(PatternFromSample(px+tx,py+ty,samp_n))) #print('pattern_two', pattern_two, px + tx, py + ty, tx, ty) self.anti_adjacency.append((pattern_one, pattern_two, (tx, ty))) - print('create antipattern', (pattern_one, pattern_two, (tx, ty))) + #print('create antipattern', (pattern_one, pattern_two, (tx, ty))) for x in range(0, self.FMX): for y in range(0, self.FMY): @@ -383,7 +390,7 @@ def Disallowed(p1, p2, dx, dy): #print(anti_adj) if (anti_adj[0] == p1 and anti_adj[1] == p2): if dx == anti_adj[2][0] and dy == anti_adj[2][1]: - #print('antipattern:',p1,p2,dx,dy,'\n',anti_adj,'\n') + print('antipattern:',p1,p2,dx,dy,'\n',anti_adj,'\n') return True return False @@ -402,7 +409,7 @@ def Agrees(p1, p2, dx, dy): for y in range(ymin, ymax): for x in range(xmin, xmax): if p1[x + self.N * y] != p2[x - dx + self.N * (y - dy)]: - logging.debug(p1[x + self.N * y] != p2[x - dx + self.N * (y - dy)]) + logger.debug(p1[x + self.N * y] != p2[x - dx + self.N * (y - dy)]) ifany = False #return False return ifany @@ -554,7 +561,7 @@ def Graphics(self, monochrome=False): if contributors > 0: bitmap_data[x + y * self.FMX] = (int(r / contributors), int(g / contributors), int(b / contributors)) else: - logging.info("INFO: No contributors") + logger.info("INFO: No contributors") bitmap_data[x + y * self.FMX] = (int(r), int(g), int(b)) result.putdata(bitmap_data) return result @@ -565,12 +572,14 @@ def Clear(self): for x in range(0, self.FMX): for t in range(0, self.T): - if t != self.ground: + #print('clear?',x,t,(t != self.ground),self.wave[x][self.FMY - 1][t],self.changes[x][self.FMY - 1], self.FMY - 1) + if not (t in self.ground): self.wave[x][self.FMY - 1][t] = False self.changes[x][self.FMY - 1] = True for y in range(0, self.FMY - 1): - self.wave[x][y][self.ground] = False + for g in range(self.ground[0], self.ground[1]): + self.wave[x][y][g] = False self.changes[x][y] = True while self.Propagate(): pass diff --git a/samples.xml b/samples.xml index 6a63575..685e008 100644 --- a/samples.xml +++ b/samples.xml @@ -1,3 +1,4 @@ - + + diff --git a/samples/anti_flower_ground.png b/samples/anti_flower_ground.png new file mode 100644 index 0000000000000000000000000000000000000000..335e538a0ebbfa6738f9d202a4592e5bd9938671 GIT binary patch literal 2804 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 XjfoHhsTbAc00000NkvXXu0mjfqq#b$ literal 0 HcmV?d00001 diff --git a/samples/blue_flowers2.png b/samples/blue_flowers2.png new file mode 100644 index 0000000000000000000000000000000000000000..0b02dfd4696e436d357be32d624318b36cea31d0 GIT binary patch literal 2825 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 bX9ETR{Pz$DLxP1z00000NkvXXu0mjfSOY%K literal 0 HcmV?d00001 diff --git a/samples/blue_flowers3.png b/samples/blue_flowers3.png new file mode 100644 index 0000000000000000000000000000000000000000..5d17408d69a96fc4444e9674c8dac397cc65b368 GIT binary patch literal 2844 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 literal 0 HcmV?d00001 diff --git a/samples/blue_flowers4.png b/samples/blue_flowers4.png new file mode 100644 index 0000000000000000000000000000000000000000..a3ff563385b22a099055999ae773b4dde11fa9c0 GIT binary patch literal 2825 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 literal 0 HcmV?d00001 diff --git a/samples/blue_flowers6.png b/samples/blue_flowers6.png new file mode 100644 index 0000000000000000000000000000000000000000..e2cf0674f9ef190d668e4da2721a8166b93c4982 GIT binary patch literal 2852 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 literal 0 HcmV?d00001 diff --git a/samples/red_flowers.png b/samples/red_flowers.png new file mode 100644 index 0000000000000000000000000000000000000000..a3ead9f7bbb0d2d07bc818ede62bbda01fe18674 GIT binary patch literal 2971 zcmV;M3uN?(P)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0002SNklk=LRUJf}2~CJ+_6AH1aB)U7mzb)@oxZsUtJ$Y- zE<$$+&+H9YT>=qj!jy14#D`0S3C(Bw?LMJtfUqIrOqf1{NFW2a2-E4Ci@u}=Ap4Af zOE7%~F$`iMvfG%@bi*0QE=0HlnS<;hOc$brAhJ@lut#w>ToGnypobta@q*%RT=9Zw zArof6VTKRR^o=!qaH__m6uW9X;lsp03Sjv)DHV&b0RvWX>}K!05RO$GFaXh6a{52% RKj{Df002ovPDHLkV1mGZf?@yw literal 0 HcmV?d00001 From a61d3a48d6ae69b7d5dcb29a031ece80186d1285 Mon Sep 17 00:00:00 2001 From: Isaac Date: Mon, 12 Mar 2018 20:50:37 -0700 Subject: [PATCH 09/11] refactor --- common.py | 46 +++++ model.py | 521 +----------------------------------------------- overlapmodel.py | 430 +++++++++++++++++++++++++++++++++++++++ program.py | 82 ++++++++ wfc.yml | 117 +++++++++++ 5 files changed, 676 insertions(+), 520 deletions(-) create mode 100644 common.py create mode 100644 overlapmodel.py create mode 100644 program.py create mode 100644 wfc.yml diff --git a/common.py b/common.py new file mode 100644 index 0000000..80cffec --- /dev/null +++ b/common.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +""" +Created on Mon Mar 12 20:43:12 2018 + +@author: Isaac +""" + +import logging +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + +def StuffRandom(source_array, random_value): + a_sum = sum(source_array) + + if 0 == a_sum: + for j in range(0, len(source_array)): + source_array[j] = 1 + a_sum = sum(source_array) + for j in range(0, len(source_array)): + source_array[j] /= a_sum + i = 0 + x = 0 + while (i < len(source_array)): + x += source_array[i] + if random_value <= x: + return i + i += 1 + return 0 + +def StuffPower(a, n): + product = 1 + for i in range(0, n): + product *= a + return product + +# TODO: finish StuffGet +def StuffGet(xml_node, xml_attribute, default_t): + s = "" + if s == "": + return default_t + return s + +def string2bool(strn): + if isinstance(strn, bool): + return strn + return strn.lower() in ["true"] diff --git a/model.py b/model.py index aa00799..6f7370b 100644 --- a/model.py +++ b/model.py @@ -11,10 +11,6 @@ import math import random -import xml.etree.ElementTree as ET -import collections -import logging -import uuid try: import Image @@ -24,10 +20,7 @@ hackstring = "" hackcount = 0 -logger = logging.getLogger(__name__) -logger.setLevel(logging.INFO) - - + class Model: def __init__(self, width, height): #initialize @@ -175,417 +168,6 @@ def OnBoundary(self, x, y): def Graphics(self): return Image.new("RGB",(self.FMX, self.FMY),(0,0,0)) -class OverlappingModel(Model): - - def __init__(self, width, height, name, N_value = 2, periodic_input_value = True, periodic_output_value = False, symmetry_value = 8, ground_value = 0, additional_samples=[], additional_periodic="", antipatterns=""): - """ - Initializes the model. - """ - super( OverlappingModel, self).__init__(width, height) - self.propagator = [[[[]]]] - self.N = N_value - self.periodic = periodic_output_value - self.bitmaps = [Image.open("samples/{0}.png".format(name)).convert("RGBA")] - self.SMXs = [self.bitmaps[0].size[0]] - self.SMYs = [self.bitmaps[0].size[1]] - self.samples = [[[0 for _ in range(self.SMYs[0])] for _ in range(self.SMXs[0])]] - - self.antipattern_flags = [int(x) for x in list(antipatterns.ljust(len(additional_samples) + 1, '0'))] - print(self.antipattern_flags) - - add_periodic = 0 - if periodic_input_value: - add_periodic = 1 - self.periodic_flags = [add_periodic] + [int(x) for x in list(additional_periodic.ljust(len(additional_samples), str(add_periodic)))] - print(self.periodic_flags) - - for additional in additional_samples: - self.bitmaps.append(Image.open("samples/{0}.png".format(additional)).convert("RGBA")) - add_index = len(self.bitmaps)-1 - self.SMXs.append(self.bitmaps[add_index].size[0]) - self.SMYs.append(self.bitmaps[add_index].size[1]) - self.samples.append([[0 for _ in range(self.SMYs[add_index])] for _ in range(self.SMXs[add_index])]) - -# self.antibitmaps = [] -# self.aSMXs = [] -# self.aSMYs = [] -# self.antisamples = [] -# for anti in antipatterns: -# self.antibitmaps.append(Image.open("samples/{0}.png".format(anti))) -# add_index = len(self.antibitmaps)-1 -# self.SMXs.append(self.antibitmaps[add_index].size[0]) -# self.SMYs.append(self.antibitmaps[add_index].size[1]) -# self.antisamples.append([[0 for _ in range(self.aSMYs[add_index])] for _ in range(self.aSMXs[add_index])]) - - - - self.colors = [] - for samp_n in range(len(self.bitmaps)): - for y in range(0, self.SMYs[samp_n]): - for x in range(0, self.SMXs[samp_n]): - a_color = self.bitmaps[samp_n].getpixel((x, y)) - color_exists = [c for c in self.colors if c == a_color] - if len(color_exists) < 1: - self.colors.append(a_color) - samp_result = [i for i,v in enumerate(self.colors) if v == a_color] - self.samples[samp_n][x][y] = samp_result - - for c in self.colors: - print(c) - - self.color_count = len(self.colors) - self.W = StuffPower(self.color_count, self.N * self.N) - - # The pattern matrix, as an array of arrays. - self.patterns= [[]] - #self.ground = 0 - - # Additional samples can individually be marked as periodic/non-periodic - #periodic_input_values = [periodic_input_value] - #for _ in range(len(self.bitmaps)): - # periodic_input_values.append(periodic_input_value) - #for idx_peri, peri in enumerate(additional_periodic): - # periodic_input_values[idx_peri + 1] = ((1 == peri) or ('T' == peri) or ('True' == peri)) - - - def FuncPattern(passed_func): - result = [0 for _ in range(self.N * self.N)] - for y in range(0, self.N): - for x in range(0, self.N): - result[x + (y * self.N)] = passed_func(x, y) - return result - - pattern_func = FuncPattern - - def PatternFromSample(x, y, n=0): - def innerPattern(dx, dy): - return self.samples[n][(x + dx) % self.SMXs[n]][(y + dy) % self.SMYs[n]] - return pattern_func(innerPattern) - def Rotate(p): - ''' - Returns a rotated version of the pattern. - ''' - return FuncPattern(lambda x, y: p[self.N - 1 - y + x * self.N]) - def Reflect(p): - ''' - Returns a reflected version of the pattern. - ''' - return FuncPattern(lambda x, y: p[self.N - 1 - x + y * self.N]) - - def Index(p): - ''' - Converts a color index into a powers-of-two representation for - bytewise storage. - ''' - result = 0 - power = 1 - for i in range(0, len(p)): - result = result + (sum(p[len(p) - 1 - i]) * power) - power = power * self.color_count - return result - - - - def PatternFromIndex(ind): - ''' - Takes a pattern index and returns the pattern byte array. - ''' - residue = ind - power = self.W - result = [None for _ in range(self.N * self.N)] - for i in range(0, len(result)): - power = power / self.color_count - count = 0 - while residue >= power: - residue = residue - power - count = count + 1 - result[i] = count - return result - - self.weights = collections.Counter() - ordering = [] - antiordering = [] - self.anti_adjacency = [] - - for samp_n in range(len(self.bitmaps)): - ylimit = self.SMYs[samp_n] - self.N + 1 - xlimit = self.SMXs[samp_n] - self.N + 1 - if 1 == self.periodic_flags[samp_n]: - ylimit = self.SMYs[samp_n] - xlimit = self.SMXs[samp_n] - for y in range (0, ylimit): - for x in range(0, xlimit): - ps = [0 for _ in range(8)] - ps[0] = PatternFromSample(x,y,samp_n) - ps[1] = Reflect(ps[0]) - ps[2] = Rotate(ps[0]) - ps[3] = Reflect(ps[2]) - ps[4] = Rotate(ps[2]) - ps[5] = Reflect(ps[4]) - ps[6] = Rotate(ps[4]) - ps[7] = Reflect(ps[6]) - for k in range(0,symmetry_value): - ind = Index(ps[k]) - logger.info('pattern: ' + str(ps[k]) + ' index ' + str(ind)) - if 1 == self.antipattern_flags[samp_n]: - if not ind in antiordering: - antiordering.append(ind) - indexed_weight = collections.Counter({ind : 1}) - self.weights = self.weights + indexed_weight - if not ind in ordering: - ordering.append(ind) - - for indo, o in enumerate(ordering): - print(indo, o, PatternFromIndex(o)) - self.T = len(self.weights) - self.ground = (102,106)# int((ground_value + self.T) % self.T) - print('self.ground',self.ground) - - self.patterns = [[None] for _ in range(self.T)] - self.stationary = [None for _ in range(self.T)] - self.propagator = [[[[0]]] for _ in range(2 * self.N - 1)] - - self.antipatterns = [[None] for _ in antiordering] - - for w in antiordering: - self.antipatterns.append(PatternFromIndex(w)) - - counter = 0 - for w in ordering: - self.patterns[counter] = PatternFromIndex(w) - self.stationary[counter] = self.weights[w] - counter += 1 - - for samp_n in range(len(self.bitmaps)): - if 1 == self.antipattern_flags[samp_n]: - #print('sample #',samp_n) - ylimit = self.SMYs[samp_n] - self.N + 1 - xlimit = self.SMXs[samp_n] - self.N + 1 - if 1 == self.periodic_flags[samp_n]: - ylimit = self.SMYs[samp_n] - xlimit = self.SMXs[samp_n] - #print('limits',xlimit, ylimit, self.periodic_flags[samp_n]) - for py in range (0, ylimit): - for px in range(0, xlimit): - pattern_one = PatternFromIndex(Index(PatternFromSample(px,py,samp_n))) - print('pattern_one', pattern_one) - for tx in range(1 - self.N, self.N): - for ty in range(1 - self.N,self.N): - #print(tx,ty) - if not (tx == 0 and ty == 0): - if (1 == self.periodic_flags[samp_n]) or ((px + tx >= 0) and (py + ty >= 0) and (px + tx < xlimit) and (py + ty < ylimit)): - pattern_two = PatternFromIndex(Index(PatternFromSample(px+tx,py+ty,samp_n))) - #print('pattern_two', pattern_two, px + tx, py + ty, tx, ty) - self.anti_adjacency.append((pattern_one, pattern_two, (tx, ty))) - #print('create antipattern', (pattern_one, pattern_two, (tx, ty))) - - for x in range(0, self.FMX): - for y in range(0, self.FMY): - self.wave[x][y] = [False for _ in range(self.T)] - - - def Disallowed(p1, p2, dx, dy): - #print(p1,p2,'\n---\n') - for anti_adj in self.anti_adjacency: - #print(anti_adj) - if (anti_adj[0] == p1 and anti_adj[1] == p2): - if dx == anti_adj[2][0] and dy == anti_adj[2][1]: - print('antipattern:',p1,p2,dx,dy,'\n',anti_adj,'\n') - return True - return False - - def Agrees(p1, p2, dx, dy): - ifany = True - xmin = dx - xmax = self.N - if dx < 0: - xmin = 0 - xmax = dx + self.N - ymin = dy - ymax = self.N - if dy < 0: - ymin = 0 - ymax = dy + self.N - for y in range(ymin, ymax): - for x in range(xmin, xmax): - if p1[x + self.N * y] != p2[x - dx + self.N * (y - dy)]: - logger.debug(p1[x + self.N * y] != p2[x - dx + self.N * (y - dy)]) - ifany = False - #return False - return ifany - #return True - - for x in range(0, 2 * self.N - 1): - #print('x',x) - self.propagator[x] = [[[0]] for _ in range(2 * self.N - 1)] - for y in range(0, 2 * self.N - 1): - #print('y',y) - self.propagator[x][y] = [[0] for _ in range(self.T)] - - for t in range(0, self.T): - #print('t',t) - a_list = [] - #print('pattern',self.patterns[t]) - for t2 in range(0, self.T): - #if not (((2 in self.patterns[t]) and (3 in self.patterns[t2])) or ((3 in self.patterns[t]) and (2 in self.patterns[t2]))): - if not Disallowed(self.patterns[t], self.patterns[t2], x - self.N + 1, y - self.N + 1): - if Agrees(self.patterns[t], self.patterns[t2], x - self.N + 1, y - self.N + 1): - a_list.append(t2) - self.propagator[x][y][t] = [0 for _ in range(len(a_list))] - for c in range(0, len(a_list)): - self.propagator[x][y][t][c] = a_list[c] - - for x in self.propagator: - for y in x: - for t in y: - #print('-----') - for c in t: - #print(c) - pass - return - - def OnBoundary(self, x, y): - return (not self.periodic) and ((x + self.N > self.FMX ) or (y + self.N > self.FMY)) - - def Propagate(self): - change = False - b = False - - #x2 = None - #y2 = None - for x1 in range(0, self.FMX): - for y1 in range(0, self.FMY): - if (self.changes[x1][y1]): - self.changes[x1][y1] = False - dx = (0 - self.N) + 1 - while dx < self.N: - #for dx in range(1 - self.N, self.N): - dy = (0 - self.N) + 1 - while dy < self.N: - #for dy in range(1 - self.N, self.N): - x2 = x1 + dx - if x2 < 0: - x2 += self.FMX - elif x2 >= self.FMX: - x2 -= self.FMX - y2 = y1 + dy - if y2 < 0: - y2 += self.FMY - elif y2 >= self.FMY: - y2 -= self.FMY - - if (not self.periodic) and (x2 + self.N > self.FMX or y2 + self.N > self.FMY): - pass - else: - - w1 = self.wave[x1][y1] - w2 = self.wave[x2][y2] - - p = self.propagator[(self.N - 1) - dx][(self.N - 1) - dy] - - for t2 in range(0,self.T): - if (not w2[t2]): - pass - else: - b = False - prop = p[t2] - #print("Prop: {0}".format(prop)) - i_one = 0 - while (i_one < len(prop)) and (False == b): - b = w1[prop[i_one]] - i_one += 1 - if False == b: - self.changes[x2][y2] = True - change = True - w2[t2] = False - dy += 1 - dx += 1 - - return change - - def Graphics(self, monochrome=False): - result = Image.new("RGB",(self.FMX, self.FMY),(0,0,0)) - bitmap_data = list(result.getdata()) - if(self.observed != None): - for y in range(0, self.FMY): - dy = self.N - 1 - if (y < (self.FMY - self.N + 1)): - dy = 0 - for x in range(0, self.FMX): - dx = 0 - if (x < (self.FMX - self.N + 1)): - dx = self.N - 1 - local_obsv = self.observed[x - dx][y - dy] - local_patt = self.patterns[local_obsv][dx + dy * self.N] - c = self.colors[local_patt] - #bitmap_data[x + y * self.FMX] = (0xff000000 | (c.R << 16) | (c.G << 8) | c.B) - if monochrome: - if isinstance(c, (int, float)): - bitmap_data[x + y * self.FMX] = (c, c, c) - else: - bitmap_data[x + y * self.FMX] = (c[0], c[1], c[2]) - else: - bitmap_data[x + y * self.FMX] = (c[0], c[1], c[2]) - - else: - for y in range(0, self.FMY): - for x in range(0, self.FMX): - contributors = 0 - r = 0 - g = 0 - b = 0 - for dy in range(0, self.N): - for dx in range(0, self.N): - sx = x - dx - if sx < 0: - sx += self.FMX - sy = y - dy - if sy < 0: - sy += self.FMY - if (self.OnBoundary(sx, sy)): - pass - else: - for t in range(0, self.T): - if self.wave[sx][sy][t]: - contributors += 1 - color = self.colors[self.patterns[t][dx + dy * self.N]] - if isinstance(color, (int, float)): - r = int(color) - g = int(color) - b = int(color) - else: - r += int(color[0])#.R - g += int(color[1])#.G - b += int(color[2])#.B - #bitmap_data[x + y * self.FMX] = (0xff000000 | ((r / contributors) << 16) | ((g / contributors) << 8) | (b / contributors)) - if contributors > 0: - bitmap_data[x + y * self.FMX] = (int(r / contributors), int(g / contributors), int(b / contributors)) - else: - logger.info("INFO: No contributors") - bitmap_data[x + y * self.FMX] = (int(r), int(g), int(b)) - result.putdata(bitmap_data) - return result - - def Clear(self): - super(OverlappingModel, self).Clear() - if(self.ground != 0 ): - - for x in range(0, self.FMX): - for t in range(0, self.T): - #print('clear?',x,t,(t != self.ground),self.wave[x][self.FMY - 1][t],self.changes[x][self.FMY - 1], self.FMY - 1) - if not (t in self.ground): - self.wave[x][self.FMY - 1][t] = False - self.changes[x][self.FMY - 1] = True - - for y in range(0, self.FMY - 1): - for g in range(self.ground[0], self.ground[1]): - self.wave[x][y][g] = False - self.changes[x][y] = True - while self.Propagate(): - pass - - - class SimpleTiledModel(Model): def __init__(self, width, height, name, subset_name, periodic_value, black_value): @@ -604,105 +186,4 @@ def __init__(self, width, height, name, subset_name, periodic_value, black_value #def getNextRandom(): # return random.random() -def StuffRandom(source_array, random_value): - a_sum = sum(source_array) - if 0 == a_sum: - for j in range(0, len(source_array)): - source_array[j] = 1 - a_sum = sum(source_array) - for j in range(0, len(source_array)): - source_array[j] /= a_sum - i = 0 - x = 0 - while (i < len(source_array)): - x += source_array[i] - if random_value <= x: - return i - i += 1 - return 0 - -def StuffPower(a, n): - product = 1 - for i in range(0, n): - product *= a - return product - -# TODO: finish StuffGet -def StuffGet(xml_node, xml_attribute, default_t): - s = "" - if s == "": - return default_t - return s - -def string2bool(strn): - if isinstance(strn, bool): - return strn - return strn.lower() in ["true"] - -class Program: - def __init__(self): - pass - - def Main(self): - self.random = random.Random() - xdoc = ET.ElementTree(file="samples.xml") - counter = 1 - for xnode in xdoc.getroot(): - if("#comment" == xnode.tag): - continue - a_model = None - - name = xnode.get('name', "NAME") - global hackstring - hackstring = name - - - - print("< {0} ".format(name), end='') - if "overlapping" == xnode.tag: - #print(xnode.attrib) - add_samp_string = xnode.get('additional', "NONE") - - add_samp = [] - if "NONE" != add_samp_string: - add_samp = add_samp_string.split(':') - - add_peri = xnode.get('additional_periodic', "") - - a_model = OverlappingModel(int(xnode.get('width', 48)), int(xnode.get('height', 48)), xnode.get('name', "NAME"), int(xnode.get('N', 2)), string2bool(xnode.get('periodicInput', True)), string2bool(xnode.get('periodic', False)), int(xnode.get('symmetry', 8)), int(xnode.get('ground',0)), additional_samples=add_samp, additional_periodic=add_peri, antipatterns=xnode.get('antipatterns', '0')) - pass - elif "simpletiled" == xnode.tag: - print("> ", end="\n") - continue - else: - continue - - - - for i in range(0, int(xnode.get("screenshots", 2))): - for k in range(0, 10): - print("> ", end="") - seed = self.random.random() - finished = a_model.Run(seed, int(xnode.get("limit", 0))) - if finished: - print("DONE") - a_model.Graphics().save("{0}_{1}_{2}_{3}.png".format(counter, name, i, uuid.uuid4()), format="PNG") - break - else: - print("CONTRADICTION") - counter += 1 - - -prog = Program() -prog.Main() - -#a_model = OverlappingModel(8, 8, "Chess", 2, True, True, 8,0) -#a_model = OverlappingModel(48, 48, "Hogs", 3, True, True, 8,0) -#gseed = random.Random() -#finished = a_model.Run(364, 0) -#if(finished): - #test_img = a_model.Graphics() -#else: -# print("CONTRADICTION") -#test_img diff --git a/overlapmodel.py b/overlapmodel.py new file mode 100644 index 0000000..9c1835e --- /dev/null +++ b/overlapmodel.py @@ -0,0 +1,430 @@ +# -*- coding: utf-8 -*- +""" +Created on Mon Mar 12 20:41:23 2018 + +@author: Isaac +""" + +import model +import common + +import collections + +from sklearn import tree + +try: + import Image +except ImportError: + from PIL import Image + +class OverlappingModel(model.Model): + + def __init__(self, width, height, name, N_value = 2, periodic_input_value = True, periodic_output_value = False, symmetry_value = 8, ground_value = 0, additional_samples=[], additional_periodic="", antipatterns=""): + """ + Initializes the model. + """ + super( OverlappingModel, self).__init__(width, height) + self.propagator = [[[[]]]] + self.N = N_value + self.periodic = periodic_output_value + self.bitmaps = [Image.open("samples/{0}.png".format(name)).convert("RGBA")] + self.SMXs = [self.bitmaps[0].size[0]] + self.SMYs = [self.bitmaps[0].size[1]] + self.samples = [[[0 for _ in range(self.SMYs[0])] for _ in range(self.SMXs[0])]] + + self.antipattern_flags = [int(x) for x in list(antipatterns.ljust(len(additional_samples) + 1, '0'))] + print(self.antipattern_flags) + + add_periodic = 0 + if periodic_input_value: + add_periodic = 1 + self.periodic_flags = [add_periodic] + [int(x) for x in list(additional_periodic.ljust(len(additional_samples), str(add_periodic)))] + print(self.periodic_flags) + + for additional in additional_samples: + self.bitmaps.append(Image.open("samples/{0}.png".format(additional)).convert("RGBA")) + add_index = len(self.bitmaps)-1 + self.SMXs.append(self.bitmaps[add_index].size[0]) + self.SMYs.append(self.bitmaps[add_index].size[1]) + self.samples.append([[0 for _ in range(self.SMYs[add_index])] for _ in range(self.SMXs[add_index])]) + +# self.antibitmaps = [] +# self.aSMXs = [] +# self.aSMYs = [] +# self.antisamples = [] +# for anti in antipatterns: +# self.antibitmaps.append(Image.open("samples/{0}.png".format(anti))) +# add_index = len(self.antibitmaps)-1 +# self.SMXs.append(self.antibitmaps[add_index].size[0]) +# self.SMYs.append(self.antibitmaps[add_index].size[1]) +# self.antisamples.append([[0 for _ in range(self.aSMYs[add_index])] for _ in range(self.aSMXs[add_index])]) + + + + self.colors = [] + for samp_n in range(len(self.bitmaps)): + for y in range(0, self.SMYs[samp_n]): + for x in range(0, self.SMXs[samp_n]): + a_color = self.bitmaps[samp_n].getpixel((x, y)) + color_exists = [c for c in self.colors if c == a_color] + if len(color_exists) < 1: + self.colors.append(a_color) + samp_result = [i for i,v in enumerate(self.colors) if v == a_color] + self.samples[samp_n][x][y] = samp_result + + for c in self.colors: + print(c) + + self.color_count = len(self.colors) + self.W = common.StuffPower(self.color_count, self.N * self.N) + + # The pattern matrix, as an array of arrays. + self.patterns= [[]] + #self.ground = 0 + + # Additional samples can individually be marked as periodic/non-periodic + #periodic_input_values = [periodic_input_value] + #for _ in range(len(self.bitmaps)): + # periodic_input_values.append(periodic_input_value) + #for idx_peri, peri in enumerate(additional_periodic): + # periodic_input_values[idx_peri + 1] = ((1 == peri) or ('T' == peri) or ('True' == peri)) + + + def FuncPattern(passed_func): + result = [0 for _ in range(self.N * self.N)] + for y in range(0, self.N): + for x in range(0, self.N): + result[x + (y * self.N)] = passed_func(x, y) + return result + + pattern_func = FuncPattern + + def PatternFromSample(x, y, n=0): + def innerPattern(dx, dy): + return self.samples[n][(x + dx) % self.SMXs[n]][(y + dy) % self.SMYs[n]] + return pattern_func(innerPattern) + def Rotate(p): + ''' + Returns a rotated version of the pattern. + ''' + return FuncPattern(lambda x, y: p[self.N - 1 - y + x * self.N]) + def Reflect(p): + ''' + Returns a reflected version of the pattern. + ''' + return FuncPattern(lambda x, y: p[self.N - 1 - x + y * self.N]) + + def Index(p): + ''' + Converts a color index into a powers-of-two representation for + bytewise storage. + ''' + result = 0 + power = 1 + for i in range(0, len(p)): + result = result + (sum(p[len(p) - 1 - i]) * power) + power = power * self.color_count + return result + + + + def PatternFromIndex(ind): + ''' + Takes a pattern index and returns the pattern byte array. + ''' + residue = ind + power = self.W + result = [None for _ in range(self.N * self.N)] + for i in range(0, len(result)): + power = power / self.color_count + count = 0 + while residue >= power: + residue = residue - power + count = count + 1 + result[i] = count + return result + + self.weights = collections.Counter() + ordering = [] + antiordering = [] + self.anti_adjacency = [] + + for samp_n in range(len(self.bitmaps)): + ylimit = self.SMYs[samp_n] - self.N + 1 + xlimit = self.SMXs[samp_n] - self.N + 1 + if 1 == self.periodic_flags[samp_n]: + ylimit = self.SMYs[samp_n] + xlimit = self.SMXs[samp_n] + for y in range (0, ylimit): + for x in range(0, xlimit): + ps = [0 for _ in range(8)] + ps[0] = PatternFromSample(x,y,samp_n) + ps[1] = Reflect(ps[0]) + ps[2] = Rotate(ps[0]) + ps[3] = Reflect(ps[2]) + ps[4] = Rotate(ps[2]) + ps[5] = Reflect(ps[4]) + ps[6] = Rotate(ps[4]) + ps[7] = Reflect(ps[6]) + for k in range(0,symmetry_value): + ind = Index(ps[k]) + common.logger.info('pattern: ' + str(ps[k]) + ' index ' + str(ind)) + if 1 == self.antipattern_flags[samp_n]: + if not ind in antiordering: + antiordering.append(ind) + indexed_weight = collections.Counter({ind : 1}) + self.weights = self.weights + indexed_weight + if not ind in ordering: + ordering.append(ind) + + for indo, o in enumerate(ordering): + print(indo, o, PatternFromIndex(o)) + self.T = len(self.weights) + self.ground = (102,106)# int((ground_value + self.T) % self.T) + print('self.ground',self.ground) + + self.patterns = [[None] for _ in range(self.T)] + self.stationary = [None for _ in range(self.T)] + self.propagator = [[[[0]]] for _ in range(2 * self.N - 1)] + + self.antipatterns = [[None] for _ in antiordering] + + for w in antiordering: + self.antipatterns.append(PatternFromIndex(w)) + + counter = 0 + for w in ordering: + self.patterns[counter] = PatternFromIndex(w) + self.stationary[counter] = self.weights[w] + counter += 1 + + for samp_n in range(len(self.bitmaps)): + if 1 == self.antipattern_flags[samp_n]: + #print('sample #',samp_n) + ylimit = self.SMYs[samp_n] - self.N + 1 + xlimit = self.SMXs[samp_n] - self.N + 1 + if 1 == self.periodic_flags[samp_n]: + ylimit = self.SMYs[samp_n] + xlimit = self.SMXs[samp_n] + #print('limits',xlimit, ylimit, self.periodic_flags[samp_n]) + for py in range (0, ylimit): + for px in range(0, xlimit): + pattern_one = PatternFromIndex(Index(PatternFromSample(px,py,samp_n))) + print('pattern_one', pattern_one) + for tx in range(1 - self.N, self.N): + for ty in range(1 - self.N,self.N): + #print(tx,ty) + if not (tx == 0 and ty == 0): + if (1 == self.periodic_flags[samp_n]) or ((px + tx >= 0) and (py + ty >= 0) and (px + tx < xlimit) and (py + ty < ylimit)): + pattern_two = PatternFromIndex(Index(PatternFromSample(px+tx,py+ty,samp_n))) + #print('pattern_two', pattern_two, px + tx, py + ty, tx, ty) + self.anti_adjacency.append((pattern_one, pattern_two, (tx, ty))) + #print('create antipattern', (pattern_one, pattern_two, (tx, ty))) + + for x in range(0, self.FMX): + for y in range(0, self.FMY): + self.wave[x][y] = [False for _ in range(self.T)] + + + def Disallowed(p1, p2, dx, dy): + #print(p1,p2,'\n---\n') + for anti_adj in self.anti_adjacency: + #print(anti_adj) + if (anti_adj[0] == p1 and anti_adj[1] == p2): + if dx == anti_adj[2][0] and dy == anti_adj[2][1]: + print('antipattern:',p1,p2,dx,dy,'\n',anti_adj,'\n') + return True + return False + + def Agrees(p1, p2, dx, dy): + ifany = True + xmin = dx + xmax = self.N + if dx < 0: + xmin = 0 + xmax = dx + self.N + ymin = dy + ymax = self.N + if dy < 0: + ymin = 0 + ymax = dy + self.N + for y in range(ymin, ymax): + for x in range(xmin, xmax): + if p1[x + self.N * y] != p2[x - dx + self.N * (y - dy)]: + common.logger.debug(p1[x + self.N * y] != p2[x - dx + self.N * (y - dy)]) + ifany = False + #return False + return ifany + #return True + + for x in range(0, 2 * self.N - 1): + #print('x',x) + self.propagator[x] = [[[0]] for _ in range(2 * self.N - 1)] + for y in range(0, 2 * self.N - 1): + #print('y',y) + self.propagator[x][y] = [[0] for _ in range(self.T)] + + for t in range(0, self.T): + #print('t',t) + a_list = [] + #print('pattern',self.patterns[t]) + for t2 in range(0, self.T): + #if not (((2 in self.patterns[t]) and (3 in self.patterns[t2])) or ((3 in self.patterns[t]) and (2 in self.patterns[t2]))): + if not Disallowed(self.patterns[t], self.patterns[t2], x - self.N + 1, y - self.N + 1): + if Agrees(self.patterns[t], self.patterns[t2], x - self.N + 1, y - self.N + 1): + a_list.append(t2) + self.propagator[x][y][t] = [0 for _ in range(len(a_list))] + for c in range(0, len(a_list)): + self.propagator[x][y][t][c] = a_list[c] + + for x in self.propagator: + for y in x: + for t in y: + #print('-----') + for c in t: + #print(c) + pass + return + + def OnBoundary(self, x, y): + return (not self.periodic) and ((x + self.N > self.FMX ) or (y + self.N > self.FMY)) + + def Propagate(self): + change = False + b = False + + #x2 = None + #y2 = None + for x1 in range(0, self.FMX): + for y1 in range(0, self.FMY): + if (self.changes[x1][y1]): + self.changes[x1][y1] = False + dx = (0 - self.N) + 1 + while dx < self.N: + #for dx in range(1 - self.N, self.N): + dy = (0 - self.N) + 1 + while dy < self.N: + #for dy in range(1 - self.N, self.N): + x2 = x1 + dx + if x2 < 0: + x2 += self.FMX + elif x2 >= self.FMX: + x2 -= self.FMX + y2 = y1 + dy + if y2 < 0: + y2 += self.FMY + elif y2 >= self.FMY: + y2 -= self.FMY + + if (not self.periodic) and (x2 + self.N > self.FMX or y2 + self.N > self.FMY): + pass + else: + + w1 = self.wave[x1][y1] + w2 = self.wave[x2][y2] + + p = self.propagator[(self.N - 1) - dx][(self.N - 1) - dy] + + for t2 in range(0,self.T): + if (not w2[t2]): + pass + else: + b = False + prop = p[t2] + #print("Prop: {0}".format(prop)) + i_one = 0 + while (i_one < len(prop)) and (False == b): + b = w1[prop[i_one]] + i_one += 1 + if False == b: + self.changes[x2][y2] = True + change = True + w2[t2] = False + dy += 1 + dx += 1 + + return change + + def Graphics(self, monochrome=False): + result = Image.new("RGB",(self.FMX, self.FMY),(0,0,0)) + bitmap_data = list(result.getdata()) + if(self.observed != None): + for y in range(0, self.FMY): + dy = self.N - 1 + if (y < (self.FMY - self.N + 1)): + dy = 0 + for x in range(0, self.FMX): + dx = 0 + if (x < (self.FMX - self.N + 1)): + dx = self.N - 1 + local_obsv = self.observed[x - dx][y - dy] + local_patt = self.patterns[local_obsv][dx + dy * self.N] + c = self.colors[local_patt] + #bitmap_data[x + y * self.FMX] = (0xff000000 | (c.R << 16) | (c.G << 8) | c.B) + if monochrome: + if isinstance(c, (int, float)): + bitmap_data[x + y * self.FMX] = (c, c, c) + else: + bitmap_data[x + y * self.FMX] = (c[0], c[1], c[2]) + else: + bitmap_data[x + y * self.FMX] = (c[0], c[1], c[2]) + + else: + for y in range(0, self.FMY): + for x in range(0, self.FMX): + contributors = 0 + r = 0 + g = 0 + b = 0 + for dy in range(0, self.N): + for dx in range(0, self.N): + sx = x - dx + if sx < 0: + sx += self.FMX + sy = y - dy + if sy < 0: + sy += self.FMY + if (self.OnBoundary(sx, sy)): + pass + else: + for t in range(0, self.T): + if self.wave[sx][sy][t]: + contributors += 1 + color = self.colors[self.patterns[t][dx + dy * self.N]] + if isinstance(color, (int, float)): + r = int(color) + g = int(color) + b = int(color) + else: + r += int(color[0])#.R + g += int(color[1])#.G + b += int(color[2])#.B + #bitmap_data[x + y * self.FMX] = (0xff000000 | ((r / contributors) << 16) | ((g / contributors) << 8) | (b / contributors)) + if contributors > 0: + bitmap_data[x + y * self.FMX] = (int(r / contributors), int(g / contributors), int(b / contributors)) + else: + common.logger.info("INFO: No contributors") + bitmap_data[x + y * self.FMX] = (int(r), int(g), int(b)) + result.putdata(bitmap_data) + return result + + def Clear(self): + super(OverlappingModel, self).Clear() + if(self.ground != 0 ): + + for x in range(0, self.FMX): + for t in range(0, self.T): + #print('clear?',x,t,(t != self.ground),self.wave[x][self.FMY - 1][t],self.changes[x][self.FMY - 1], self.FMY - 1) + if not (t in self.ground): + self.wave[x][self.FMY - 1][t] = False + self.changes[x][self.FMY - 1] = True + + for y in range(0, self.FMY - 1): + for g in range(self.ground[0], self.ground[1]): + self.wave[x][y][g] = False + self.changes[x][y] = True + while self.Propagate(): + pass + + + diff --git a/program.py b/program.py new file mode 100644 index 0000000..8bbc656 --- /dev/null +++ b/program.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +""" +Created on Mon Mar 12 20:45:58 2018 + +@author: Isaac +""" + +import random +import xml.etree.ElementTree as ET +import uuid + +import common +import model +import overlapmodel + + +class Program: + def __init__(self): + pass + + def Main(self): + self.random = random.Random() + xdoc = ET.ElementTree(file="samples.xml") + counter = 1 + for xnode in xdoc.getroot(): + if("#comment" == xnode.tag): + continue + a_model = None + + name = xnode.get('name', "NAME") + global hackstring + hackstring = name + + + + print("< {0} ".format(name), end='') + if "overlapping" == xnode.tag: + #print(xnode.attrib) + add_samp_string = xnode.get('additional', "NONE") + + add_samp = [] + if "NONE" != add_samp_string: + add_samp = add_samp_string.split(':') + + add_peri = xnode.get('additional_periodic', "") + + a_model = overlapmodel.OverlappingModel(int(xnode.get('width', 48)), int(xnode.get('height', 48)), xnode.get('name', "NAME"), int(xnode.get('N', 2)), common.string2bool(xnode.get('periodicInput', True)), common.string2bool(xnode.get('periodic', False)), int(xnode.get('symmetry', 8)), int(xnode.get('ground',0)), additional_samples=add_samp, additional_periodic=add_peri, antipatterns=xnode.get('antipatterns', '0')) + pass + elif "simpletiled" == xnode.tag: + print("> ", end="\n") + continue + else: + continue + + + + for i in range(0, int(xnode.get("screenshots", 2))): + for k in range(0, 10): + print("> ", end="") + seed = self.random.random() + finished = a_model.Run(seed, int(xnode.get("limit", 0))) + if finished: + print("DONE") + a_model.Graphics().save("{0}_{1}_{2}_{3}.png".format(counter, name, i, uuid.uuid4()), format="PNG") + break + else: + print("CONTRADICTION") + counter += 1 + + +prog = Program() +prog.Main() + +#a_model = OverlappingModel(8, 8, "Chess", 2, True, True, 8,0) +#a_model = OverlappingModel(48, 48, "Hogs", 3, True, True, 8,0) +#gseed = random.Random() +#finished = a_model.Run(364, 0) +#if(finished): + #test_img = a_model.Graphics() +#else: +# print("CONTRADICTION") +#test_img diff --git a/wfc.yml b/wfc.yml new file mode 100644 index 0000000..ced8c18 --- /dev/null +++ b/wfc.yml @@ -0,0 +1,117 @@ +name: wfc +channels: +- anaconda-fusion +- defaults +dependencies: +- alabaster=0.7.10=py36hcd07829_0 +- asn1crypto=0.24.0=py36_0 +- astroid=1.6.0=py36_0 +- babel=2.5.3=py36_0 +- bleach=2.1.2=py36_0 +- ca-certificates=2017.08.26=h94faf87_0 +- certifi=2018.1.18=py36_0 +- cffi=1.11.4=py36hfa6e2cd_0 +- chardet=3.0.4=py36h420ce6e_1 +- cloudpickle=0.5.2=py36h6b1d831_0 +- colorama=0.3.9=py36h029ae33_0 +- cryptography=2.1.4=py36he1d7878_0 +- decorator=4.2.1=py36_0 +- docutils=0.14=py36h6012d8f_0 +- entrypoints=0.2.3=py36hfd66bb0_2 +- freetype=2.8=h51f8f2c_1 +- html5lib=1.0.1=py36h047fa9f_0 +- icc_rt=2017.0.4=h97af966_0 +- icu=58.2=ha66f8fd_1 +- idna=2.6=py36h148d497_1 +- imagesize=0.7.1=py36he29f638_0 +- intel-openmp=2018.0.0=hd92c6cd_8 +- ipykernel=4.8.0=py36_0 +- ipython=6.2.1=py36h9cf0123_1 +- ipython_genutils=0.2.0=py36h3c5d0ee_0 +- isort=4.2.15=py36h6198cc5_0 +- jedi=0.11.1=py36_0 +- jinja2=2.10=py36h292fed1_0 +- jpeg=9b=hb83a4c4_2 +- jsonschema=2.6.0=py36h7636477_0 +- jupyter_client=5.2.2=py36_0 +- jupyter_core=4.4.0=py36h56e9d50_0 +- lazy-object-proxy=1.3.1=py36hd1c21d2_0 +- libpng=1.6.34=h79bbb47_0 +- libtiff=4.0.9=h0f13578_0 +- markupsafe=1.0=py36h0e26971_1 +- mccabe=0.6.1=py36hb41005a_1 +- mistune=0.8.3=py36_0 +- mkl=2018.0.1=h2108138_4 +- nbconvert=5.3.1=py36h8dc0fde_0 +- nbformat=4.4.0=py36h3a5bc1b_0 +- numpy=1.14.1=py36hb69e940_2 +- numpydoc=0.7.0=py36ha25429e_0 +- olefile=0.44=py36h0a7bdd2_0 +- openssl=1.0.2n=h74b6da3_0 +- pandoc=1.19.2.1=hb2460c7_1 +- pandocfilters=1.4.2=py36h3ef6317_1 +- parso=0.1.1=py36hae3edee_0 +- pickleshare=0.7.4=py36h9de030f_0 +- pillow=5.0.0=py36h0738816_0 +- pip=9.0.1=py36h226ae91_4 +- prompt_toolkit=1.0.15=py36h60b8f86_0 +- psutil=5.4.3=py36hfa6e2cd_0 +- pycodestyle=2.3.1=py36h7cc55cd_0 +- pycparser=2.18=py36hd053e01_1 +- pyflakes=1.6.0=py36h0b975d6_0 +- pygments=2.2.0=py36hb010967_0 +- pylint=1.8.1=py36_0 +- pyopenssl=17.5.0=py36h5b7d817_0 +- pyqt=5.6.0=py36hb5ed885_5 +- pysocks=1.6.7=py36h698d350_1 +- python=3.6.4=h6538335_1 +- python-dateutil=2.6.1=py36h509ddcb_1 +- pytz=2017.3=py36h1d3fa6b_0 +- pyzmq=16.0.3=py36he714bf5_0 +- qt=5.6.2=vc14h6f8c307_12 +- qtawesome=0.4.4=py36h5aa48f6_0 +- qtconsole=4.3.1=py36h99a29a9_0 +- qtpy=1.3.1=py36hb8717c5_0 +- requests=2.18.4=py36h4371aae_1 +- rope=0.10.7=py36had63a69_0 +- scikit-learn=0.19.1=py36h53aea1b_0 +- scipy=1.0.0=py36h1260518_0 +- setuptools=38.4.0=py36_0 +- simplegeneric=0.8.1=py36heab741f_0 +- sip=4.18.1=py36h9c25514_2 +- six=1.11.0=py36h4db2310_1 +- snowballstemmer=1.2.1=py36h763602f_0 +- sphinx=1.6.6=py36_0 +- sphinxcontrib=1.0=py36hbbac3d2_1 +- sphinxcontrib-websupport=1.0.1=py36hb5e5916_1 +- spyder=3.2.6=py36_0 +- sqlite=3.21.0=h9d3ae62_2 +- testpath=0.3.1=py36h2698cfe_0 +- tk=8.6.7=hcb92d03_3 +- tornado=4.5.3=py36_0 +- traitlets=4.3.2=py36h096827d_0 +- typing=3.6.2=py36hb035bda_0 +- urllib3=1.22=py36h276f60a_0 +- vc=14=h0510ff6_3 +- vs2015_runtime=14.0.25123=3 +- wcwidth=0.1.7=py36h3d5aa90_0 +- webencodings=0.5.1=py36h67c50ae_1 +- wheel=0.30.0=py36h6c3ec14_1 +- win_inet_pton=1.0.1=py36he67d7fd_1 +- wincertstore=0.2=py36h7fe50ca_0 +- wrapt=1.10.11=py36he5f5981_0 +- zlib=1.2.11=h8395fce_2 +- pip: + - appdirs==1.4.3 + - exifread==2.1.2 + - imagehash==4.0 + - ipython-genutils==0.2.0 + - jupyter-client==5.2.2 + - jupyter-core==4.4.0 + - prompt-toolkit==1.0.15 + - pymzn==0.16.5 + - pywavelets==0.5.2 + - pyyaml==3.12 + - win-inet-pton==1.0.1 +prefix: C:\Software\Anaconda3\envs\wfc + From cdc5d48e908f5a5946b33b73901eea7d0951131a Mon Sep 17 00:00:00 2001 From: Isaac Date: Mon, 12 Mar 2018 21:03:57 -0700 Subject: [PATCH 10/11] fix missing import --- model.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/model.py b/model.py index 6f7370b..da00f08 100644 --- a/model.py +++ b/model.py @@ -9,6 +9,8 @@ # The software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software. # +import common + import math import random @@ -115,7 +117,7 @@ def Observe(self): distribution = [0 for _ in range(0,self.T)] for t in range(0,self.T): distribution[t] = self.stationary[t] if self.wave[argminx][argminy][t] else 0 - r = StuffRandom(distribution, self.rng.random()) + r = common.StuffRandom(distribution, self.rng.random()) for t in range(0,self.T): self.wave[argminx][argminy][t] = (t == r) self.changes[argminx][argminy] = True From d6540d41dae42e3aec225250554f333fbaaec8d5 Mon Sep 17 00:00:00 2001 From: Isaac Date: Wed, 14 Mar 2018 23:06:10 -0700 Subject: [PATCH 11/11] Dehydrate rehydrate --- learning.xml | 5 + learnmodel.py | 516 ++++++++++++++++++++++++++++++++++++++++++++++++ model.py | 9 +- overlapmodel.py | 8 +- program.py | 19 +- samples.xml | 6 +- samples1.xml | 11 ++ 7 files changed, 564 insertions(+), 10 deletions(-) create mode 100644 learning.xml create mode 100644 learnmodel.py create mode 100644 samples1.xml diff --git a/learning.xml b/learning.xml new file mode 100644 index 0000000..0d25603 --- /dev/null +++ b/learning.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/learnmodel.py b/learnmodel.py new file mode 100644 index 0000000..173562f --- /dev/null +++ b/learnmodel.py @@ -0,0 +1,516 @@ +# -*- coding: utf-8 -*- +""" +Created on Mon Mar 12 20:41:23 2018 + +@author: Isaac +""" + +import model +import common + +import collections + +from sklearn import tree + +try: + import Image +except ImportError: + from PIL import Image + +class LearningModel(model.Model): + + def __init__(self, width, height, name, N_value = 2, periodic_input_value = True, periodic_output_value = False, symmetry_value = 8, ground_value = 0, ground_end = 0, additional_samples=[], additional_periodic="", antipatterns=""): + """ + Initializes the model. + """ + super(LearningModel, self).__init__(width, height) + self.propagator = [[[[]]]] + self.whitelist = [[[[]]]] + self.blacklist = [[[[]]]] + self.possiblelist = [[[[]]]] + self.allowedlist = [[[[]]]] + self.disallowedlist = [[[[]]]] + self.observedlist= [[[[]]]] + + self.N = N_value + self.periodic = periodic_output_value + self.bitmaps = [Image.open("samples/{0}.png".format(name)).convert("RGBA")] + self.SMXs = [self.bitmaps[0].size[0]] + self.SMYs = [self.bitmaps[0].size[1]] + self.samples = [[[0 for _ in range(self.SMYs[0])] for _ in range(self.SMXs[0])]] + + self.antipattern_flags = [int(x) for x in list(antipatterns.ljust(len(additional_samples) + 1, '0'))] + #print(self.antipattern_flags) + + add_periodic = 0 + if periodic_input_value: + add_periodic = 1 + self.periodic_flags = [add_periodic] + [int(x) for x in list(additional_periodic.ljust(len(additional_samples), str(add_periodic)))] + #print(self.periodic_flags) + + for additional in additional_samples: + self.bitmaps.append(Image.open("samples/{0}.png".format(additional)).convert("RGBA")) + add_index = len(self.bitmaps)-1 + self.SMXs.append(self.bitmaps[add_index].size[0]) + self.SMYs.append(self.bitmaps[add_index].size[1]) + self.samples.append([[0 for _ in range(self.SMYs[add_index])] for _ in range(self.SMXs[add_index])]) + +# self.antibitmaps = [] +# self.aSMXs = [] +# self.aSMYs = [] +# self.antisamples = [] +# for anti in antipatterns: +# self.antibitmaps.append(Image.open("samples/{0}.png".format(anti))) +# add_index = len(self.antibitmaps)-1 +# self.SMXs.append(self.antibitmaps[add_index].size[0]) +# self.SMYs.append(self.antibitmaps[add_index].size[1]) +# self.antisamples.append([[0 for _ in range(self.aSMYs[add_index])] for _ in range(self.aSMXs[add_index])]) + + + + self.colors = [] + for samp_n in range(len(self.bitmaps)): + for y in range(0, self.SMYs[samp_n]): + for x in range(0, self.SMXs[samp_n]): + a_color = self.bitmaps[samp_n].getpixel((x, y)) + color_exists = [c for c in self.colors if c == a_color] + if len(color_exists) < 1: + self.colors.append(a_color) + samp_result = [i for i,v in enumerate(self.colors) if v == a_color] + self.samples[samp_n][x][y] = samp_result + + #for c in self.colors: + # print(c) + + self.color_count = len(self.colors) + self.W = common.StuffPower(self.color_count, self.N * self.N) + + # The pattern matrix, as an array of arrays. + self.patterns= [[]] + #self.ground = 0 + + # Additional samples can individually be marked as periodic/non-periodic + #periodic_input_values = [periodic_input_value] + #for _ in range(len(self.bitmaps)): + # periodic_input_values.append(periodic_input_value) + #for idx_peri, peri in enumerate(additional_periodic): + # periodic_input_values[idx_peri + 1] = ((1 == peri) or ('T' == peri) or ('True' == peri)) + + + def FuncPattern(passed_func): + result = [0 for _ in range(self.N * self.N)] + for y in range(0, self.N): + for x in range(0, self.N): + result[x + (y * self.N)] = passed_func(x, y) + return result + + pattern_func = FuncPattern + + def PatternFromSample(x, y, n=0): + def innerPattern(dx, dy): + return self.samples[n][(x + dx) % self.SMXs[n]][(y + dy) % self.SMYs[n]] + return pattern_func(innerPattern) + def Rotate(p): + ''' + Returns a rotated version of the pattern. + ''' + return FuncPattern(lambda x, y: p[self.N - 1 - y + x * self.N]) + def Reflect(p): + ''' + Returns a reflected version of the pattern. + ''' + return FuncPattern(lambda x, y: p[self.N - 1 - x + y * self.N]) + + def Index(p): + ''' + Converts a color index into a powers-of-two representation for + bytewise storage. + ''' + result = 0 + power = 1 + for i in range(0, len(p)): + result = result + (sum(p[len(p) - 1 - i]) * power) + power = power * self.color_count + return result + + + + def PatternFromIndex(ind): + ''' + Takes a pattern index and returns the pattern byte array. + ''' + residue = ind + power = self.W + result = [None for _ in range(self.N * self.N)] + for i in range(0, len(result)): + power = power / self.color_count + count = 0 + while residue >= power: + residue = residue - power + count = count + 1 + result[i] = count + return result + + self.weights = collections.Counter() + ordering = [] + antiordering = [] + self.anti_adjacency = [] + + for samp_n in range(len(self.bitmaps)): + ylimit = self.SMYs[samp_n] - self.N + 1 + xlimit = self.SMXs[samp_n] - self.N + 1 + if 1 == self.periodic_flags[samp_n]: + ylimit = self.SMYs[samp_n] + xlimit = self.SMXs[samp_n] + for y in range (0, ylimit): + for x in range(0, xlimit): + ps = [0 for _ in range(8)] + ps[0] = PatternFromSample(x,y,samp_n) + ps[1] = Reflect(ps[0]) + ps[2] = Rotate(ps[0]) + ps[3] = Reflect(ps[2]) + ps[4] = Rotate(ps[2]) + ps[5] = Reflect(ps[4]) + ps[6] = Rotate(ps[4]) + ps[7] = Reflect(ps[6]) + for k in range(0,symmetry_value): + ind = Index(ps[k]) + common.logger.info('pattern: ' + str(ps[k]) + ' index ' + str(ind)) + if 1 == self.antipattern_flags[samp_n]: + if not ind in antiordering: + antiordering.append(ind) + indexed_weight = collections.Counter({ind : 1}) + self.weights = self.weights + indexed_weight + if not ind in ordering: + ordering.append(ind) + + #for indo, o in enumerate(ordering): + # print(indo, o, PatternFromIndex(o)) + self.T = len(self.weights) + self.ground = (int((ground_value + self.T) % self.T), int((ground_value + self.T) % self.T))#(102,106)# int((ground_value + self.T) % self.T) + if ground_end > 0 and None != ground_end: + self.gound = (ground_value, ground_end) + #print('self.ground',self.ground) + + self.patterns = [[None] for _ in range(self.T)] + self.stationary = [None for _ in range(self.T)] + self.propagator = [[[[0]]] for _ in range(2 * self.N - 1)] + self.whitelist = [[[[0]]] for _ in range(2 * self.N - 1)] + self.blacklist = [[[[0]]] for _ in range(2 * self.N - 1)] + self.possiblelist = [[[[0]]] for _ in range(2 * self.N - 1)] + self.allowedlist = [[[[0]]] for _ in range(2 * self.N - 1)] + self.disallowedlist = [[[[0]]] for _ in range(2 * self.N - 1)] + self.observedlist = [[[[0]]] for _ in range(2 * self.N - 1)] + + self.antipatterns = [[None] for _ in antiordering] + + for w in antiordering: + self.antipatterns.append(PatternFromIndex(w)) + + counter = 0 + for w in ordering: + self.patterns[counter] = PatternFromIndex(w) + self.stationary[counter] = self.weights[w] + counter += 1 + + + + for samp_n in range(len(self.bitmaps)): + if 1 == self.antipattern_flags[samp_n]: + #print('sample #',samp_n) + ylimit = self.SMYs[samp_n] - self.N + 1 + xlimit = self.SMXs[samp_n] - self.N + 1 + if 1 == self.periodic_flags[samp_n]: + ylimit = self.SMYs[samp_n] + xlimit = self.SMXs[samp_n] + #print('limits',xlimit, ylimit, self.periodic_flags[samp_n]) + for py in range (0, ylimit): + for px in range(0, xlimit): + pattern_one = PatternFromIndex(Index(PatternFromSample(px,py,samp_n))) + #print('pattern_one', pattern_one) + for tx in range(1 - self.N, self.N): + for ty in range(1 - self.N,self.N): + #print(tx,ty) + if not (tx == 0 and ty == 0): + if (1 == self.periodic_flags[samp_n]) or ((px + tx >= 0) and (py + ty >= 0) and (px + tx < xlimit) and (py + ty < ylimit)): + pattern_two = PatternFromIndex(Index(PatternFromSample(px+tx,py+ty,samp_n))) + #print('pattern_two', pattern_two, px + tx, py + ty, tx, ty) + self.anti_adjacency.append((pattern_one, pattern_two, (tx, ty))) + #print('create antipattern', (pattern_one, pattern_two, (tx, ty))) + + for x in range(0, self.FMX): + for y in range(0, self.FMY): + self.wave[x][y] = [False for _ in range(self.T)] + + + def Disallowed(p1, p2, dx, dy): + #print(p1,p2,'\n---\n') + for anti_adj in self.anti_adjacency: + #print(anti_adj) + if (anti_adj[0] == p1 and anti_adj[1] == p2): + if dx == anti_adj[2][0] and dy == anti_adj[2][1]: + #print('antipattern:',p1,p2,dx,dy,'\n',anti_adj,'\n') + return True + return False + + def Agrees(p1, p2, dx, dy): + ifany = True + xmin = dx + xmax = self.N + if dx < 0: + xmin = 0 + xmax = dx + self.N + ymin = dy + ymax = self.N + if dy < 0: + ymin = 0 + ymax = dy + self.N + for y in range(ymin, ymax): + for x in range(xmin, xmax): + if p1[x + self.N * y] != p2[x - dx + self.N * (y - dy)]: + common.logger.debug(p1[x + self.N * y] != p2[x - dx + self.N * (y - dy)]) + ifany = False + #return False + return ifany + #return True + + # create total whitelist + for x in range(0, 2 * self.N - 1): + self.possiblelist[x] = [[[0]] for _ in range(2 * self.N - 1)] + for y in range(0, 2 * self.N - 1): + self.possiblelist[x][y] = [[0] for _ in range(self.T)] + for t in range(0, self.T): + a_list = [] + for t2 in range(0, self.T): + a_list.append(t2) + self.possiblelist[x][y][t] = [0 for _ in range(len(a_list))] + for c in range(0, len(a_list)): + self.possiblelist[x][y][t][c] = a_list[c] + + + + for x in range(0, 2 * self.N - 1): + self.observedlist[x] = [[[0]] for _ in range(2 * self.N - 1)] + self.allowedlist[x] = [[[0]] for _ in range(2 * self.N - 1)] + self.disallowedlist[x] = [[[0]] for _ in range(2 * self.N - 1)] + for y in range(0, 2 * self.N - 1): + self.observedlist[x][y] = [[0] for _ in range(self.T)] + self.allowedlist[x][y] = [[0] for _ in range(self.T)] + self.disallowedlist[x][y] = [[0] for _ in range(self.T)] + + for t in range(0, self.T): + o_list = [] + a_list = [] + d_list = [] + for t2 in range(0, self.T): + if not Disallowed(self.patterns[t], self.patterns[t2], x - self.N + 1, y - self.N + 1): + if Agrees(self.patterns[t], self.patterns[t2], x - self.N + 1, y - self.N + 1): + a_list.append(t2) + o_list.append(t2) + else: + d_list.append(t2) + if Agrees(self.patterns[t], self.patterns[t2], x - self.N + 1, y - self.N + 1): + a_list.append(t2) + self.observedlist[x][y][t] = [0 for _ in range(len(o_list))] + for c in range(0, len(o_list)): + self.observedlist[x][y][t][c] = o_list[c] + self.allowedlist[x][y][t] = [0 for _ in range(len(a_list))] + for c in range(0, len(a_list)): + self.allowedlist[x][y][t][c] = a_list[c] + self.disallowedlist[x][y][t] = [0 for _ in range(len(d_list))] + for c in range(0, len(d_list)): + self.disallowedlist[x][y][t][c] = d_list[c] + + self.propagator = self.observedlist.copy() + #print(self.FlattenPropagator(self.observedlist)) + self.propagator = self.UnflattenPropagator(self.FlattenPropagator(self.observedlist)).copy() +# print() +# for ix, x in enumerate(self.propagator): +# for iy, y in enumerate(x): +# for it, t in enumerate(y): +# print(ix, ',', iy, ':', it, '>>', t) + + return + + def FlattenPropagator(self, prop): + def Dehydrate(node): + return '{}_{}_{}'.format(node[0], node[1], node[2]) + flatprop = [] + for x in range(0, 2 * self.N - 1): + for y in range(0, 2 * self.N - 1): + for t in range(0, self.T): + for t2 in prop[x][y][t]: + flatprop.append([Dehydrate([t, x, y]), t2]) + prop_dict = collections.defaultdict(list) + for x in flatprop: + prop_dict[x[0]].append(x[1]) + + return prop_dict.copy() + + def UnflattenPropagator(self, prop_dict): + prop = [] + for d in prop_dict: + for p in prop_dict[d]: + prop.append([d, p]) + hydratedlist = [[[[0]]] for _ in range(2 * self.N - 1)] + for x in range(0, 2 * self.N - 1): + hydratedlist[x] = [[[0]] for _ in range(2 * self.N - 1)] + for y in range(0, 2 * self.N - 1): + hydratedlist[x][y] = [[0] for _ in range(self.T)] + for t in range(0, self.T): + hydratedlist[x][y][t] = [] + def Rehydrate(node): + return [int(x) for x in node.split('_')] + for p in prop: + t, x, y = Rehydrate(p[0]) + hydratedlist[x][y][t].append(p[1]) + + + return hydratedlist + + def OnBoundary(self, x, y): + return (not self.periodic) and ((x + self.N > self.FMX ) or (y + self.N > self.FMY)) + + def Propagate(self): + change = False + b = False + + #x2 = None + #y2 = None + for x1 in range(0, self.FMX): + for y1 in range(0, self.FMY): + if (self.changes[x1][y1]): + self.changes[x1][y1] = False + dx = (0 - self.N) + 1 + while dx < self.N: + #for dx in range(1 - self.N, self.N): + dy = (0 - self.N) + 1 + while dy < self.N: + #for dy in range(1 - self.N, self.N): + x2 = x1 + dx + if x2 < 0: + x2 += self.FMX + elif x2 >= self.FMX: + x2 -= self.FMX + y2 = y1 + dy + if y2 < 0: + y2 += self.FMY + elif y2 >= self.FMY: + y2 -= self.FMY + + if (not self.periodic) and (x2 + self.N > self.FMX or y2 + self.N > self.FMY): + pass + else: + + w1 = self.wave[x1][y1] + w2 = self.wave[x2][y2] + + p = self.propagator[(self.N - 1) - dx][(self.N - 1) - dy] + + for t2 in range(0,self.T): + if (not w2[t2]): + pass + else: + b = False + prop = p[t2] + #print("Prop: {0}".format(prop)) + i_one = 0 + while (i_one < len(prop)) and (False == b): + b = w1[prop[i_one]] + i_one += 1 + if False == b: + self.changes[x2][y2] = True + change = True + w2[t2] = False + dy += 1 + dx += 1 + + return change + + def Graphics(self, monochrome=True): + result = Image.new("RGB",(self.FMX, self.FMY),(0,0,0)) + bitmap_data = list(result.getdata()) + if(self.observed != None): + print(self.observed) + print(self.patterns) + for y in range(0, self.FMY): + dy = self.N - 1 + if (y < (self.FMY - self.N + 1)): + dy = 0 + for x in range(0, self.FMX): + dx = 0 + if (x < (self.FMX - self.N + 1)): + dx = self.N - 1 + local_obsv = self.observed[x - dx][y - dy] + local_patt = self.patterns[local_obsv][dx + dy * self.N] + c = self.colors[local_patt] + #bitmap_data[x + y * self.FMX] = (0xff000000 | (c.R << 16) | (c.G << 8) | c.B) + if isinstance(c, (int, float)): + bitmap_data[x + y * self.FMX] = (c, c, c) + else: + bitmap_data[x + y * self.FMX] = (c[0], c[1], c[2]) + + else: + for y in range(0, self.FMY): + for x in range(0, self.FMX): + contributors = 0 + r = 0 + g = 0 + b = 0 + for dy in range(0, self.N): + for dx in range(0, self.N): + sx = x - dx + if sx < 0: + sx += self.FMX + sy = y - dy + if sy < 0: + sy += self.FMY + if (self.OnBoundary(sx, sy)): + pass + else: + for t in range(0, self.T): + if self.wave[sx][sy][t]: + contributors += 1 + color = self.colors[self.patterns[t][dx + dy * self.N]] + if isinstance(color, (int, float)): + r = int(color) + g = int(color) + b = int(color) + else: + r += int(color[0])#.R + g += int(color[1])#.G + b += int(color[2])#.B + #bitmap_data[x + y * self.FMX] = (0xff000000 | ((r / contributors) << 16) | ((g / contributors) << 8) | (b / contributors)) + if contributors > 0: + bitmap_data[x + y * self.FMX] = (int(r / contributors), int(g / contributors), int(b / contributors)) + else: + common.logger.info("INFO: No contributors") + bitmap_data[x + y * self.FMX] = (int(r), int(g), int(b)) + result.putdata(bitmap_data) + return result + + def Clear(self): + super(LearningModel, self).Clear() + if(self.ground != (0,0) ): + + for x in range(0, self.FMX): + for t in range(0, self.T): + #print('clear?',x,t,(t != self.ground),self.wave[x][self.FMY - 1][t],self.changes[x][self.FMY - 1], self.FMY - 1) + if not (t in self.ground): + self.wave[x][self.FMY - 1][t] = False + self.changes[x][self.FMY - 1] = True + + for y in range(0, self.FMY - 1): + for g in range(self.ground[0], self.ground[1]): + print(self.wave) + print(self.wave[x]) + print(self.wave[x][y]) + print(x,y,g) + print(self.wave[x][y][g]) + self.wave[x][y][g] = False + self.changes[x][y] = True + while self.Propagate(): + pass + + + diff --git a/model.py b/model.py index da00f08..fdbd0f3 100644 --- a/model.py +++ b/model.py @@ -49,7 +49,7 @@ def __init__(self, width, height): self.count_prop_passes = 0 - self.SAVE_IN_PROGRESS = False + self.SAVE_IN_PROGRESS = True def Observe(self): self.observe_count += 1 @@ -100,10 +100,14 @@ def Observe(self): observed_min = entropy + noise argminx = x argminy = y - + def TrueIndex(cont): + return [idx for idx, i in enumerate(cont) if i] + # No minimum entropy, so mark everything as being observed... if (-1 == argminx) and (-1 == argminy): self.observed = [[0 for _ in range(self.FMY)] for _ in range(self.FMX)] + print('self.wave',[[TrueIndex(y) for y in x] for x in self.wave]) + for x in range(0, self.FMX): self.observed[x] = [0 for _ in range(self.FMY)] for y in range(0, self.FMY): @@ -111,6 +115,7 @@ def Observe(self): if self.wave[x][y][t]: self.observed[x][y] = t break + print('self.observed', self.observed) return True # A minimum point has been found, so prep it for propogation... diff --git a/overlapmodel.py b/overlapmodel.py index 9c1835e..2374b3d 100644 --- a/overlapmodel.py +++ b/overlapmodel.py @@ -19,7 +19,7 @@ class OverlappingModel(model.Model): - def __init__(self, width, height, name, N_value = 2, periodic_input_value = True, periodic_output_value = False, symmetry_value = 8, ground_value = 0, additional_samples=[], additional_periodic="", antipatterns=""): + def __init__(self, width, height, name, N_value = 2, periodic_input_value = True, periodic_output_value = False, symmetry_value = 8, ground_value = 0, ground_end = 0, additional_samples=[], additional_periodic="", antipatterns=""): """ Initializes the model. """ @@ -180,7 +180,9 @@ def PatternFromIndex(ind): for indo, o in enumerate(ordering): print(indo, o, PatternFromIndex(o)) self.T = len(self.weights) - self.ground = (102,106)# int((ground_value + self.T) % self.T) + self.ground = (int((ground_value + self.T) % self.T), int((ground_value + self.T) % self.T))#(102,106)# int((ground_value + self.T) % self.T) + if ground_end > 0 and None != ground_end: + self.gound = (ground_value, ground_end) print('self.ground',self.ground) self.patterns = [[None] for _ in range(self.T)] @@ -410,7 +412,7 @@ def Graphics(self, monochrome=False): def Clear(self): super(OverlappingModel, self).Clear() - if(self.ground != 0 ): + if(self.ground != (0,0) ): for x in range(0, self.FMX): for t in range(0, self.T): diff --git a/program.py b/program.py index 8bbc656..94af0c3 100644 --- a/program.py +++ b/program.py @@ -12,6 +12,7 @@ import common import model import overlapmodel +import learnmodel class Program: @@ -20,7 +21,7 @@ def __init__(self): def Main(self): self.random = random.Random() - xdoc = ET.ElementTree(file="samples.xml") + xdoc = ET.ElementTree(file="learning.xml") counter = 1 for xnode in xdoc.getroot(): if("#comment" == xnode.tag): @@ -34,7 +35,7 @@ def Main(self): print("< {0} ".format(name), end='') - if "overlapping" == xnode.tag: + if "learning" == xnode.tag: #print(xnode.attrib) add_samp_string = xnode.get('additional', "NONE") @@ -44,7 +45,19 @@ def Main(self): add_peri = xnode.get('additional_periodic', "") - a_model = overlapmodel.OverlappingModel(int(xnode.get('width', 48)), int(xnode.get('height', 48)), xnode.get('name', "NAME"), int(xnode.get('N', 2)), common.string2bool(xnode.get('periodicInput', True)), common.string2bool(xnode.get('periodic', False)), int(xnode.get('symmetry', 8)), int(xnode.get('ground',0)), additional_samples=add_samp, additional_periodic=add_peri, antipatterns=xnode.get('antipatterns', '0')) + a_model = learnmodel.LearningModel(int(xnode.get('width', 48)), int(xnode.get('height', 48)), xnode.get('name', "NAME"), int(xnode.get('N', 2)), common.string2bool(xnode.get('periodicInput', True)), common.string2bool(xnode.get('periodic', False)), int(xnode.get('symmetry', 8)), int(xnode.get('ground',0)), int(xnode.get('ground_end',0)), additional_samples=add_samp, additional_periodic=add_peri, antipatterns=xnode.get('antipatterns', '0')) + pass + elif "overlapping" == xnode.tag: + #print(xnode.attrib) + add_samp_string = xnode.get('additional', "NONE") + + add_samp = [] + if "NONE" != add_samp_string: + add_samp = add_samp_string.split(':') + + add_peri = xnode.get('additional_periodic', "") + + a_model = overlapmodel.OverlappingModel(int(xnode.get('width', 48)), int(xnode.get('height', 48)), xnode.get('name', "NAME"), int(xnode.get('N', 2)), common.string2bool(xnode.get('periodicInput', True)), common.string2bool(xnode.get('periodic', False)), int(xnode.get('symmetry', 8)), int(xnode.get('ground',0)), int(xnode.get('ground_end',0)), additional_samples=add_samp, additional_periodic=add_peri, antipatterns=xnode.get('antipatterns', '0')) pass elif "simpletiled" == xnode.tag: print("> ", end="\n") diff --git a/samples.xml b/samples.xml index 685e008..b967c0d 100644 --- a/samples.xml +++ b/samples.xml @@ -1,4 +1,6 @@ - - + + + + diff --git a/samples1.xml b/samples1.xml new file mode 100644 index 0000000..5409f8d --- /dev/null +++ b/samples1.xml @@ -0,0 +1,11 @@ + + + + + + + + + + +