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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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('--quiet', '-q', action='store_const',
  48. default=False, const=True)
  49. parser_gen = subparsers.add_parser('generate', help='evolving help')
  50. parser_gen.add_argument('--prog', '-P', type=str, default=None)
  51. parser_gen.add_argument('--output', '-o', type=str, default='gte.png')
  52. parser_gen.add_argument('--world-height', '-y', type=int, metavar='HEIGHT',
  53. default=512)
  54. parser_gen.add_argument('--world-width', '-x', type=int, metavar='WIDTH',
  55. default=512)
  56. parser_gen.add_argument('--turmit-count', '-T', type=int, metavar='N',
  57. default=3)
  58. parser_gen.add_argument('--steps', '-s', type=int, metavar='N',
  59. default=30000)
  60. args = parser.parse_args()
  61. if 'pool_size' in args:
  62. # Evolver
  63. if args.prog is None:
  64. progs = [rpnlib.RpnExpr.random(args.prog_size)
  65. for _ in range(args.pool_size)]
  66. else:
  67. prog = rpnlib.RpnExpr.from_string(args.prog)
  68. progs = [mutate(prog, force=True) for _ in range(args.pool_size-1)]
  69. generation = 0
  70. pool = Pool(args.threads)
  71. if args.log_progs is not None:
  72. logprogs = open(args.log_progs, 'a')
  73. logprogs.write('%s Run args %s\n' % (datetime.datetime.now(),
  74. sys.argv))
  75. logprogs.write('%s\n' % args)
  76. logprogs.close()
  77. else:
  78. logger.warning('No log specified (--log-progs or -L)')
  79. while True:
  80. if args.max_generation > 0 and generation >= args.max_generation:
  81. exit(0)
  82. scores = []
  83. msg = 'Gen#%d Running %d eval with %d turmits and %d steps for each %d progs'
  84. logger.info(msg % (generation, args.repeat_eval, args.turmit_count,
  85. args.steps, args.pool_size))
  86. genstart = time.time()
  87. # Preparing work
  88. works = []
  89. for pid, prog in enumerate(progs):
  90. for i in range(args.repeat_eval):
  91. works.append((generation, pid, prog, i, args))
  92. # running jobs
  93. res = pool.imap(eval_prog, works)
  94. # Processing results
  95. scores = [0 for _ in range(len(progs))]
  96. for pid, score in res:
  97. scores[pid] += score
  98. scores = [(scores[i] / args.repeat_eval, progs[i])
  99. for i in range(len(progs))]
  100. genstop = time.time()
  101. # Displaying eval results
  102. logger.info('Generation evaluating ended in %.2fs' % (genstop - genstart))
  103. scores = sorted(scores, key=lambda x: x[0], reverse=True)
  104. for i, (score, prog) in enumerate(scores):
  105. logger.info('P%d %.3f : "%s"' % (i, score, str(prog)))
  106. if args.log_progs is not None:
  107. with open(args.log_progs, 'a') as logprogs:
  108. for i, (score, prog) in enumerate(scores):
  109. msg = 'Gen #%d P%d %.3f : "%s"\n'
  110. logprogs.write(msg % (generation, i, score, str(prog)))
  111. logprogs.flush()
  112. logprogs.write('\n')
  113. # Split pool & mutate the one we kept
  114. keep_div = args.pool_div
  115. progs = []
  116. for prog in [ prog for _, prog in scores[:len(scores)//keep_div]]:
  117. progs.append(prog)
  118. for cur_mut in range(keep_div - 1):
  119. progs.append(mutate(prog, force=True, mutcount= 2**cur_mut))
  120. if len(progs) >= args.pool_size:
  121. break
  122. if len(progs) >= args.pool_size:
  123. break
  124. for pid, prog in enumerate(progs):
  125. logger.debug('P%d : %s' % (pid, str(prog)))
  126. generation += 1
  127. exit(0)
  128. else:
  129. # Generate
  130. w = World(args.world_height, args.world_width)
  131. prog = rpnlib.RpnExpr.from_string(args.prog)
  132. turmits = [LivingTurmit(world=w, prog=prog)
  133. for _ in range(args.turmit_count)]
  134. msg = 'Generating image for program %s with %d steps and %d turmits'
  135. logger.info(msg % (str(prog), args.steps, args.turmit_count))
  136. start = time.time()
  137. for step in range(args.steps):
  138. if step % 512 == 1:
  139. msg = 'Step %d/%d %dus/step fractdim=%.3f'
  140. msg %= (step, args.steps,
  141. ((time.time() - start)*1000000)//step//len(turmits),
  142. w.fractdim())
  143. logger.info(msg)
  144. for turmit in turmits:
  145. turmit()
  146. stop = time.time()
  147. msg = 'Fractdim %.3f after %d steps in %.2fs (%dus per step)'
  148. msg %= (w.fractdim(), args.steps,
  149. stop - start,
  150. ((stop - start)*1000000)//args.steps//args.turmit_count)
  151. logger.info(msg)
  152. w.save(args.output)
  153. logger.info('Image saved in %s' % args.output)
  154. exit(0)