Browse Source

Break up synthesis and optimization

master
Forest Belton 2 years ago
parent
commit
e655d6c0fc
2 changed files with 92 additions and 29 deletions
  1. +6
    -8
      ex.py
  2. +86
    -21
      gbso/optimize.py

+ 6
- 8
ex.py View File

@ -6,7 +6,7 @@ from gbso.program.test_case import TestCase
from gbso.cpu.insn import * from gbso.cpu.insn import *
from gbso.cpu.regs import R8 from gbso.cpu.regs import R8
from gbso.optimize import cost, optimize
from gbso.optimize import OptimizationParameters, cost, optimize
from gbso.program.program import Program from gbso.program.program import Program
prgm = Program( prgm = Program(
@ -20,8 +20,8 @@ prgm = Program(
outputs = [R8.A] outputs = [R8.A]
test_cases = [TestCase()] test_cases = [TestCase()]
max_size = 4
num_iters = 1_000_000
params = OptimizationParameters(max_size=4)
initial_cost = cost(prgm, test_cases, outputs, prgm) initial_cost = cost(prgm, test_cases, outputs, prgm)
initial_cycles = prgm.perf() initial_cycles = prgm.perf()
@ -36,11 +36,9 @@ start_time = time()
optimized_prgm = optimize( optimized_prgm = optimize(
prgm, prgm,
init_prgm=create_random_program(max_size),
max_size=max_size,
test_cases=test_cases,
outputs=outputs,
num_iters=num_iters,
test_cases,
outputs,
params,
) )
end_time = time() end_time = time()

+ 86
- 21
gbso/optimize.py View File

@ -1,14 +1,18 @@
from dataclasses import dataclass, replace
from math import log from math import log
from random import random from random import random
from typing import List, Optional, Tuple
from typing import Callable, List, Optional, Tuple
from gbso.program.test_case import Output, TestCase, eq_on_testcase from gbso.program.test_case import Output, TestCase, eq_on_testcase
from gbso.program.mutate import mutate_program
from gbso.program.mutate import create_random_program, mutate_program
from gbso.program.program import Program from gbso.program.program import Program
EPSILON = 0.00001 EPSILON = 0.00001
DEFAULT_NUM_ITERS = 1_000_000
DEFAULT_ANNEALING_CONSTANT = 0.5
DEFAULT_SYNTHESIS_ITERS = 0
DEFAULT_OPTIMIZE_ITERS = 5_000_000
DEFAULT_NUM_CANDIDATES = 1
DEFAULT_PROB_OPCODE = 0.25 DEFAULT_PROB_OPCODE = 0.25
DEFAULT_PROB_OPERAND = 0.25 DEFAULT_PROB_OPERAND = 0.25
@ -18,7 +22,9 @@ DEFAULT_PROB_INSN = 0.25
DEFAULT_PROB_INSN_UNUSED = 0.1 DEFAULT_PROB_INSN_UNUSED = 0.1
def cost(orig_prgm, test_cases, outputs, prgm) -> Tuple[int, bool]:
def cost(
orig_prgm: Program, test_cases: List[TestCase], outputs: List[Output], prgm: Program
) -> Tuple[float, bool]:
# Since each instruction executes in 4*k cycles (for some k), this can have # Since each instruction executes in 4*k cycles (for some k), this can have
# the undesirable effect of performance improvements being weighted much # the undesirable effect of performance improvements being weighted much
# higher than correctness. This hurts convergence pretty badly, so we scale # higher than correctness. This hurts convergence pretty badly, so we scale
@ -32,35 +38,63 @@ def cost(orig_prgm, test_cases, outputs, prgm) -> Tuple[int, bool]:
return perf + eq, eq == 0 return perf + eq, eq == 0
def optimize(
def cost_noperf(
orig_prgm: Program, test_cases: List[TestCase], outputs: List[Output], prgm: Program
) -> Tuple[float, bool]:
eq = 0
for test_case in test_cases:
eq += eq_on_testcase(orig_prgm, prgm, test_case, outputs)
return eq, eq == 0
@dataclass
class OptimizationParameters:
max_size: int
beta: float = DEFAULT_ANNEALING_CONSTANT
synthesis_iters: int = DEFAULT_SYNTHESIS_ITERS
optimize_iters: int = DEFAULT_OPTIMIZE_ITERS
num_candidates: int = DEFAULT_NUM_CANDIDATES
prob_opcode: float = DEFAULT_PROB_OPCODE
prob_operand: float = DEFAULT_PROB_OPERAND
prob_swap: float = DEFAULT_PROB_SWAP
prob_insn: float = DEFAULT_PROB_INSN
prob_insn_unused: float = DEFAULT_PROB_INSN_UNUSED
cost_fn: Callable[
[Program, List[TestCase], List[Output], Program], Tuple[float, bool]
] = cost
# Perform one round of optimization
def _optimize(
target_prgm: Program, target_prgm: Program,
max_size: int,
test_cases: List[TestCase], test_cases: List[TestCase],
outputs: List[Output], outputs: List[Output],
beta: int = 0.5, # How far away in cost you are allowed to search
params: OptimizationParameters,
num_iters: int = DEFAULT_OPTIMIZE_ITERS,
init_prgm: Optional[Program] = None, init_prgm: Optional[Program] = None,
num_iters: int = DEFAULT_NUM_ITERS,
prob_opcode: float = DEFAULT_PROB_OPCODE,
prob_operand: float = DEFAULT_PROB_OPERAND,
prob_swap: float = DEFAULT_PROB_SWAP,
prob_insn: float = DEFAULT_PROB_INSN,
prob_insn_unused: float = DEFAULT_PROB_INSN_UNUSED,
) -> Program: ) -> Program:
padded_prgm = (init_prgm or target_prgm).pad(max_size)
padded_prgm = target_prgm.pad(params.max_size)
if init_prgm is not None:
padded_prgm = init_prgm.pad(params.max_size)
last_prgm = padded_prgm last_prgm = padded_prgm
last_cost, _last_eq = cost(target_prgm, test_cases, outputs, last_prgm)
last_cost, _last_eq = params.cost_fn(target_prgm, test_cases, outputs, last_prgm)
best_prgm = target_prgm.pad(max_size)
best_cost = 0
best_prgm = target_prgm.pad(params.max_size)
best_cost = 0.0
num_candidates = 0 num_candidates = 0
for _ in range(num_iters): for _ in range(num_iters):
candidate_prgm = mutate_program( candidate_prgm = mutate_program(
last_prgm, prob_opcode, prob_operand, prob_swap, prob_insn, prob_insn_unused
last_prgm,
params.prob_opcode,
params.prob_operand,
params.prob_swap,
params.prob_insn,
params.prob_insn_unused,
) )
candidate_cost, candidate_eq = cost(
candidate_cost, candidate_eq = params.cost_fn(
target_prgm, test_cases, outputs, candidate_prgm target_prgm, test_cases, outputs, candidate_prgm
) )
@ -69,9 +103,40 @@ def optimize(
best_cost = candidate_cost best_cost = candidate_cost
num_candidates += 1 num_candidates += 1
if candidate_cost < last_cost - log(random()) / beta:
if candidate_cost < last_cost - log(random()) / params.beta:
last_prgm = candidate_prgm last_prgm = candidate_prgm
last_cost = candidate_cost last_cost = candidate_cost
print(f"Optimization complete. Total candidates: {num_candidates}")
return best_prgm return best_prgm
def optimize(
target_prgm: Program,
test_cases: List[TestCase],
outputs: List[Output],
params: OptimizationParameters,
) -> Program:
print("Synthesizing candidates...")
candidates = [
_optimize(
target_prgm,
test_cases,
outputs,
replace(params, cost_fn=cost_noperf),
num_iters=params.synthesis_iters,
init_prgm=create_random_program(params.max_size),
)
for _ in range(params.num_candidates)
]
best_candidate = min(
candidates, key=lambda p: cost(target_prgm, test_cases, outputs, p)[0]
)
print("Optimizing...")
return _optimize(
target_prgm,
test_cases,
outputs,
params,
num_iters=params.optimize_iters,
init_prgm=best_candidate,
)

Loading…
Cancel
Save