gameboy superoptimizer
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.

77 lines
2.4 KiB

2 years ago
2 years ago
  1. from math import log
  2. from random import random
  3. from typing import List, Optional, Tuple
  4. from gbso.program.test_case import Output, TestCase, eq_on_testcase
  5. from gbso.program.mutate import mutate_program
  6. from gbso.program.program import Program
  7. EPSILON = 0.00001
  8. DEFAULT_NUM_ITERS = 1_000_000
  9. DEFAULT_PROB_OPCODE = 0.25
  10. DEFAULT_PROB_OPERAND = 0.25
  11. DEFAULT_PROB_SWAP = 0.25
  12. DEFAULT_PROB_INSN = 0.25
  13. DEFAULT_PROB_INSN_UNUSED = 0.1
  14. def cost(orig_prgm, test_cases, outputs, prgm) -> Tuple[int, bool]:
  15. # Since each instruction executes in 4*k cycles (for some k), this can have
  16. # the undesirable effect of performance improvements being weighted much
  17. # higher than correctness. This hurts convergence pretty badly, so we scale
  18. # by 1/4 to compensate.
  19. perf = (prgm.perf() - orig_prgm.perf()) / 4.0
  20. eq = 0
  21. for test_case in test_cases:
  22. eq += eq_on_testcase(orig_prgm, prgm, test_case, outputs)
  23. return perf + eq, eq == 0
  24. def optimize(
  25. target_prgm: Program,
  26. max_size: int,
  27. test_cases: List[TestCase],
  28. outputs: List[Output],
  29. beta: int = 0.5, # How far away in cost you are allowed to search
  30. init_prgm: Optional[Program] = None,
  31. num_iters: int = DEFAULT_NUM_ITERS,
  32. prob_opcode: float = DEFAULT_PROB_OPCODE,
  33. prob_operand: float = DEFAULT_PROB_OPERAND,
  34. prob_swap: float = DEFAULT_PROB_SWAP,
  35. prob_insn: float = DEFAULT_PROB_INSN,
  36. prob_insn_unused: float = DEFAULT_PROB_INSN_UNUSED,
  37. ) -> Program:
  38. padded_prgm = (init_prgm or target_prgm).pad(max_size)
  39. last_prgm = padded_prgm
  40. last_cost, _last_eq = cost(target_prgm, test_cases, outputs, last_prgm)
  41. best_prgm = target_prgm.pad(max_size)
  42. best_cost = 0
  43. num_candidates = 0
  44. for _ in range(num_iters):
  45. candidate_prgm = mutate_program(
  46. last_prgm, prob_opcode, prob_operand, prob_swap, prob_insn, prob_insn_unused
  47. )
  48. candidate_cost, candidate_eq = cost(
  49. target_prgm, test_cases, outputs, candidate_prgm
  50. )
  51. if candidate_cost < best_cost and candidate_eq:
  52. best_prgm = candidate_prgm
  53. best_cost = candidate_cost
  54. num_candidates += 1
  55. if candidate_cost < last_cost - log(random()) / beta:
  56. last_prgm = candidate_prgm
  57. last_cost = candidate_cost
  58. print(f"Optimization complete. Total candidates: {num_candidates}")
  59. return best_prgm