Advertisement
Guest User

Untitled

a guest
Aug 30th, 2015
78
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.63 KB | None | 0 0
  1. #!/usr/bin/env python
  2.  
  3. # We need this for the cross-product we'll use later
  4. import itertools
  5. import random
  6.  
  7. class Language:
  8.     def __init__(self):
  9.         self.lang_m2s = {}
  10.         self.lang_s2m = {}
  11.  
  12.     # Input: A meaning, can be any Python object
  13.     # Output: The symbol chosen by the language to represent the meaning
  14.     #    The symbol will not overlap with any other symbol in the language
  15.     def learn(self, meaning):
  16.         pass
  17.  
  18.     # Input: A meaning
  19.     # Output: The symbol associated with that meaning
  20.     def speak(self, meaning):
  21.         return self.lang_m2s[meaning] if meaning in self.lang_m2s else None
  22.  
  23.     # Input: A symbol
  24.     # Output: The meaning associated with that symbol
  25.     def hear(self, symbol):
  26.         return self.lang_s2m[symbol] if symbol in self.lang_s2m else None
  27.  
  28.     def symbols(self):
  29.         return self.lang_s2m.keys()
  30.  
  31.     def meanings(self):
  32.         return self.lang_m2s.keys()
  33.  
  34.     # Returns the proportion of meanings without a unique symbol
  35.     def ambiguity(self):
  36.         # NUmber of ambiguous meanings
  37.         n = 0
  38.         for symbol in self.lang_s2m:
  39.             meanings = self.lang_s2m[symbol]
  40.             if len(meanings) > 1:
  41.                 n += len(meanings)
  42.         return n / (1.0 * len(self.lang_m2s))
  43.  
  44.     # Input: A symbol to perturb
  45.     # Output: Perturbed symbol
  46.     def perturb_symbol(self, s):
  47.         pass
  48.  
  49.     # Input: probability of SOME symbol changing in SOME way
  50.     # Output: New symbol, the internal state is updated to reflect
  51.     #    the new symbol's replacing of the old one.
  52.     def perturb(self, p):
  53.         r = random.random()
  54.         if r < p:
  55.             # Change something
  56.             # First, choose a random symbol
  57.             s = random.sample(self.lang_s2m.keys(), 1)[0]
  58.             s_old = s
  59.             m = self.lang_s2m[s]
  60.  
  61.             # We need to remove the old symbol from the language
  62.             del self.lang_s2m[s]
  63.  
  64.             s = self.perturb_symbol(s)
  65.  
  66.             # Now, add the new perturbed symbol to the language.
  67.             # If the symbol is already there, add the meaning to the list
  68.             if s in self.lang_s2m:
  69.                 # In Python, the increment operator also concatenates lists
  70.                 self.lang_s2m[s] += m
  71.             else:
  72.                 self.lang_s2m[s] = m
  73.  
  74.             for meaning in m:
  75.                 self.lang_m2s[meaning] = s
  76.  
  77.             return (s_old, s)
  78.         else:
  79.             return None
  80.  
  81.     def __repr__(self):
  82.         return __str__(self)
  83.  
  84. class MoreNumbers(Language):
  85.     # Example: A0, B000, D0
  86.     valid_letters = [ chr(ord('A') + i) for i in range(26) ]
  87.     max_zeros = 10
  88.  
  89.     def learn(self, meaning):
  90.         # Don't relearn meanings
  91.         symbol = self.speak(meaning)
  92.         if symbol != None:
  93.             return symbol
  94.  
  95.         while True:
  96.             symbol = random.sample(self.valid_letters, 1)[0] + "".join('0' * random.randint(1, self.max_zeros))
  97.             if (symbol not in self.lang_s2m) or (len(self.lang_s2m) == 0):
  98.                 break
  99.  
  100.         self.lang_s2m[symbol] = [ meaning ]
  101.         self.lang_m2s[meaning] = symbol
  102.  
  103.     def perturb_symbol(self, s):
  104.         # Randomly add/subtract a zero
  105.         # If we didn't do that, randomly twiddle the character up/down one (wrap at a-1, z+1)
  106.         r = random.random()
  107.         if r > 0.5:
  108.             # Change the letter
  109.             l = s[0]
  110.  
  111.             # This is ugly, but it's some modular arithmetic to wrap around the alphabet
  112.             l = chr(ord('a') + (ord(l) + 1 - ord('a')) % 26)
  113.             s = l + s[1:]
  114.         else:
  115.             # Change the number of zeroes
  116.             r = random.randint(0,1)
  117.             z = "".join('0'*(len(s) - 1 + (2 * r - 1)))
  118.  
  119.             if len(z) == 0:
  120.                 z = "0"
  121.             s = s[0] + z
  122.  
  123.         return s
  124.  
  125.     def __str__(self):
  126.         return "MoreNumbers"
  127.  
  128. class NumberFirst(Language):
  129.     # Example: 0a, 8f, 1s
  130.     valid_letters = [ chr(ord('a') + i) for i in range(26) ]
  131.     valid_numbers = [ chr(ord('0') + i) for i in range(10) ]
  132.  
  133.     def learn(self, meaning):
  134.         # Don't relearn meanings
  135.         symbol = self.speak(meaning)
  136.         if symbol != None:
  137.             return symbol
  138.  
  139.         while True:
  140.             symbol = random.sample(self.valid_numbers, 1)[0] + random.sample(self.valid_letters, 1)[0]
  141.             if (symbol not in self.lang_s2m) or (len(self.lang_s2m) == 0):
  142.                 break
  143.  
  144.         self.lang_s2m[symbol] = [ meaning ]
  145.         self.lang_m2s[meaning] = symbol
  146.  
  147.     def perturb_symbol(self, s):
  148.         r = random.random()
  149.         if r > 0.5:
  150.             # Change the letter
  151.             l = s[1]
  152.  
  153.             # This is ugly, but it's some modular arithmetic to wrap around the alphabet
  154.             l = chr(ord('a') + (ord(l) + 1 - ord('a')) % 26)
  155.             s = s[0] + l
  156.         else:
  157.             # Change the number
  158.             n = s[0]
  159.  
  160.             # This is ugly, but it's some modular arithmetic to wrap around the alphabet
  161.             n = chr(ord('0') + (ord(n) + 1 - ord('0')) % 10)
  162.             s = n + s[1]
  163.  
  164.         return s
  165.  
  166.     def __str__(self):
  167.         return "NumberFirst"
  168.  
  169. class NumberLast(Language):
  170.     # Example: a0, g7, t1
  171.     valid_letters = [ chr(ord('a') + i) for i in range(26) ]
  172.     valid_numbers = [ chr(ord('0') + i) for i in range(10) ]
  173.  
  174.     def learn(self, meaning):
  175.         # Don't relearn meanings
  176.         symbol = self.speak(meaning)
  177.         if symbol != None:
  178.             return symbol
  179.  
  180.         while True:
  181.             symbol = random.sample(self.valid_letters, 1)[0] + random.sample(self.valid_numbers, 1)[0]
  182.             if (symbol not in self.lang_s2m) or (len(self.lang_s2m) == 0):
  183.                 break
  184.  
  185.         self.lang_s2m[symbol] = [ meaning ]
  186.         self.lang_m2s[meaning] = symbol
  187.  
  188.     def perturb_symbol(self, s):
  189.         r = random.random()
  190.         if r > 0.5:
  191.             # Change the letter
  192.             l = s[0]
  193.  
  194.             # This is ugly, but it's some modular arithmetic to wrap around the alphabet
  195.             l = chr(ord('a') + (ord(l) + 1 - ord('a')) % 26)
  196.             s = l + s[1]
  197.         else:
  198.             # Change the number
  199.             n = s[1]
  200.  
  201.             # This is ugly, but it's some modular arithmetic to wrap around the alphabet
  202.             n = chr(ord('0') + (ord(n) + 1 - ord('0')) % 10)
  203.             s = s[0] + n
  204.  
  205.         return s
  206.  
  207.     def __str__(self):
  208.         return "NumberLast"
  209.  
  210. # Instantiate some languages
  211. langs = [ MoreNumbers(), NumberFirst(), NumberLast() ]
  212.  
  213. # Here, our 'meanings' are arbitrary, as long as they are common, and each language knows them
  214. # We can just use integers
  215. meanings = range(100)
  216.  
  217. # Now, teach the languages some stuff
  218. for m in meanings:
  219.     for l in langs:
  220.         l.learn(m)
  221.  
  222. # Now we want to do a quick check that for each meaning, there's no overlap
  223. # If we've build out languages properly, that should be true.
  224. for m in meanings:
  225.     for l1, l2 in itertools.product(langs, langs):
  226.         # If it's the same language for both the speaker and listener, we expect it
  227.         # to work out
  228.         if l1 == l2:
  229.             heard_meaning = l2.hear(l1.speak(m))
  230.             if m not in heard_meaning:
  231.                 print "Language %s didn't understand %s properly (%s)" % (str(l1), str(m), str(heard_meaning))
  232.             #else:
  233.                 #print "Language %s is OK" % str(l1)
  234.         # If it's different languages, this should fail
  235.         else:
  236.             heard_meaning = l2.hear(l1.speak(m))
  237.             if heard_meaning != None:
  238.                 print "Languages %s and %s have overlap with meaning %s" % (str(l1), str(l2), str(m))
  239.             #else:
  240.                 #print "Language %s successfully heard %s spoken by %s" (str(l2), str(m), str(l1))
  241.  
  242. import copy
  243. langs_orig = copy.deepcopy(langs)
  244.  
  245. # Create a few generations of each language, changing one symbol each time.
  246. for l in langs:
  247.     #print
  248.     #print str(l)
  249.     #print "Starting:", l.lang_s2m
  250.     for i in range(15):
  251.         r = l.perturb(0.5)
  252.         #if r != None:
  253.             #print "Perturbed %s to %s" % (str(r[0]), str(r[1]))
  254.         #print "Generation %d:" % (i+1), l.lang_s2m
  255.  
  256. # Now, compare this child language with the ancestor, and see how many meanings
  257. # can be unambiguously conveyed
  258. for l_old, l_new in zip(langs_orig, langs):
  259.     n = 0
  260.     for m in meanings:
  261.         # Try to speak the meaning in the old language to the new language
  262.         m_new = l_new.hear(l_old.speak(m))
  263.         if m_new != None and len(m_new) == 1 and m in m_new:
  264.             n += 1
  265.     print "Language %s has %g%% meanings still available" % (str(l_old), 100.0 * n / len(meanings))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement