Genetic Turmit Evolver
python
c
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

__main__.py 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. import sys
  2. import os
  3. import os.path
  4. import argparse
  5. import logging
  6. import time
  7. import datetime
  8. import numpy as np
  9. from random import randint
  10. from multiprocessing import Pool
  11. logging.basicConfig(level=logging.INFO,
  12. format="%(created)d %(asctime)s:%(module)s(%(lineno)d):%(levelname)s:%(message)s")
  13. logger = logging.getLogger()
  14. logger.debug('Logger started')
  15. from . import turmit, rpnlib
  16. from .turmit import Turmit
  17. from .world import World, LivingTurmit, eval_prog
  18. from .mutator import mutate
  19. parser = argparse.ArgumentParser(description='Genetic Turmit Evolver')
  20. subparsers = parser.add_subparsers(help='sub-commands help')
  21. parser_evolve = subparsers.add_parser('evolve', help='evolving help')
  22. parser_evolve.add_argument('--steps', '-s', type=int, metavar='N',
  23. default=30000, help="Number of steps")
  24. parser_evolve.add_argument('--repeat-eval', '-R', type=int, metavar='N',
  25. default=5)
  26. parser_evolve.add_argument('--world-height', '-y', type=int, metavar='HEIGHT',
  27. default=512)
  28. parser_evolve.add_argument('--world-width', '-x', type=int, metavar='WIDTH',
  29. default=512)
  30. parser_evolve.add_argument('--turmit-count', '-T', type=int, metavar='N',
  31. default=3)
  32. parser_evolve.add_argument('--pool-size', '-p', type=int, metavar='N',
  33. default=10)
  34. parser_evolve.add_argument('--pool-div', '-D', type=int, metavar='N',
  35. default=2,
  36. help='Each generation keep 1/N of the pool')
  37. parser_evolve.add_argument('--prog-size', type=int, metavar='SIZE',
  38. default=5)
  39. parser_evolve.add_argument('--prog', '-P', type=str, metavar='EXPR',
  40. default=None)
  41. parser_evolve.add_argument('--threads', type=int, metavar='N',
  42. default=os.cpu_count())
  43. parser_evolve.add_argument('--max-generation', '-G', type=int, metavar='N',
  44. default=0)
  45. parser_evolve.add_argument('--log-progs', '-L', type=str, metavar='FILENAME',
  46. default=None)
  47. parser_evolve.add_argument('--exp-mutate', '-E', action='store_const',
  48. default=False, const=True,
  49. help="If True make 2**copy_num mutation")
  50. parser_evolve.add_argument('--mod-steps', '-M', action='store_const',
  51. default=False, const=True,
  52. help="If True make steps change given try number")
  53. parser_evolve.add_argument('--quiet', '-q', action='store_const',
  54. default=False, const=True)
  55. parser_gen = subparsers.add_parser('generate', help='evolving help')
  56. parser_gen.add_argument('--prog', '-P', type=str, default=None)
  57. parser_gen.add_argument('--output', '-o', type=str, default='gte.png')
  58. parser_gen.add_argument('--world-height', '-y', type=int, metavar='HEIGHT',
  59. default=512)
  60. parser_gen.add_argument('--world-width', '-x', type=int, metavar='WIDTH',
  61. default=512)
  62. parser_gen.add_argument('--turmit-count', '-T', type=int, metavar='N',
  63. default=3)
  64. parser_gen.add_argument('--steps', '-s', type=int, metavar='N',
  65. default=30000)
  66. parser_gen.add_argument('--gray', '-G', action='store_const',
  67. default=False, const=True)
  68. args = parser.parse_args()
  69. if 'pool_size' in args:
  70. # Evolver
  71. if args.prog is None:
  72. prog = rpnlib.RpnExpr.random(args.prog_size)
  73. progs = [mutate(prog, force=True) for _ in range(args.pool_size-1)]
  74. #progs = [rpnlib.RpnExpr.random(args.prog_size)
  75. # for _ in range(args.pool_size)]
  76. else:
  77. prog = rpnlib.RpnExpr.from_string(args.prog)
  78. progs = [prog]
  79. progs += [mutate(prog, force=True) for _ in range(args.pool_size-1)]
  80. generation = 0
  81. pool = Pool(args.threads)
  82. if args.log_progs is not None:
  83. logprogs = open(args.log_progs, 'a')
  84. logprogs.write('%s Run args %s\n' % (datetime.datetime.now(),
  85. sys.argv))
  86. logprogs.write('%s\n' % args)
  87. logprogs.close()
  88. else:
  89. logger.warning('No log specified (--log-progs or -L)')
  90. while True:
  91. if args.max_generation > 0 and generation >= args.max_generation:
  92. exit(0)
  93. scores = []
  94. msg = 'Gen#%d Running %d eval with %d turmits and %d steps for each %d progs'
  95. logger.info(msg % (generation, args.repeat_eval, args.turmit_count,
  96. args.steps, args.pool_size))
  97. genstart = time.time()
  98. # Preparing work
  99. works = []
  100. for pid, prog in enumerate(progs):
  101. for i in range(args.repeat_eval):
  102. works.append((generation, pid, prog, i, args))
  103. # running jobs
  104. res = pool.imap(eval_prog, works)
  105. # Processing results
  106. scores = [0 for _ in range(len(progs))]
  107. for pid, score in res:
  108. scores[pid] += score
  109. scores = [(scores[i] / args.repeat_eval, progs[i])
  110. for i in range(len(progs))]
  111. genstop = time.time()
  112. # Displaying eval results
  113. logger.info('Generation evaluating ended in %.2fs' % (genstop - genstart))
  114. scores = sorted(scores, key=lambda x: x[0], reverse=True)
  115. for i, (score, prog) in enumerate(scores):
  116. logger.info('P%d %.3f : "%s"' % (i, score, str(prog)))
  117. if args.log_progs is not None:
  118. with open(args.log_progs, 'a') as logprogs:
  119. for i, (score, prog) in enumerate(scores):
  120. msg = 'Gen #%d P%d %.3f : "%s"\n'
  121. logprogs.write(msg % (generation, i, score, str(prog)))
  122. logprogs.flush()
  123. logprogs.write('\n')
  124. # Split pool & mutate the one we kept
  125. keep_div = args.pool_div
  126. progs = []
  127. for prog in [ prog for _, prog in scores[:len(scores)//keep_div]]:
  128. progs.append(prog)
  129. for cur_mut in range(keep_div - 1):
  130. if args.exp_mutate:
  131. progs.append(mutate(prog, force=True, mutcount=2**cur_mut))
  132. else:
  133. progs.append(mutate(prog, force=True, mutcount=1))
  134. if len(progs) >= args.pool_size:
  135. break
  136. if len(progs) >= args.pool_size:
  137. break
  138. for pid, prog in enumerate(progs):
  139. logger.debug('P%d : %s' % (pid, str(prog)))
  140. generation += 1
  141. exit(0)
  142. else:
  143. # Generate
  144. w = World(args.world_height, args.world_width, gray=args.gray)
  145. prog = rpnlib.RpnExpr.from_string(args.prog)
  146. turmits = [LivingTurmit(world=w, prog=prog)
  147. for _ in range(args.turmit_count)]
  148. msg = 'Generating image for program %s with %d steps and %d turmits'
  149. logger.info(msg % (str(prog), args.steps, args.turmit_count))
  150. start = time.time()
  151. for step in range(args.steps):
  152. if step % 512 == 1:
  153. msg = 'Step %d/%d %dus/step fractdim=%.3f'
  154. msg %= (step, args.steps,
  155. ((time.time() - start)*1000000)//step//len(turmits),
  156. w.fractdim())
  157. logger.info(msg)
  158. for turmit in turmits:
  159. turmit()
  160. stop = time.time()
  161. msg = 'Fractdim %.3f after %d steps in %.2fs (%dus per step)'
  162. msg %= (w.fractdim(), args.steps,
  163. stop - start,
  164. ((stop - start)*1000000)//args.steps//args.turmit_count)
  165. logger.info(msg)
  166. w.save(args.output)
  167. logger.info('Image saved in %s' % args.output)
  168. exit(0)