Files
subcoding/grow/definition.go

268 lines
5.1 KiB
Go
Raw Normal View History

2021-11-20 19:38:31 -10:00
package grow
2021-11-20 18:51:19 -10:00
import "io"
2021-11-24 20:08:47 -08:00
import "math/rand"
2021-11-24 19:52:22 -08:00
import "sort"
2021-11-20 18:51:19 -10:00
import "gopkg.in/yaml.v2"
2021-11-20 20:17:56 -10:00
import "github.com/firestuff/subcoding/vm"
2021-11-20 18:51:19 -10:00
type Definition struct {
2021-11-23 20:45:03 -08:00
GlobalMemorySize uint64 `yaml:"global_memory_size"`
FunctionMemorySize uint64 `yaml:"function_memory_size"`
InstructionLimit uint64 `yaml:"instruction_limit"`
InstructionsPerFunctionMean uint64 `yaml:"instructions_per_function_mean"`
InstructionsPerFunctionStdDev uint64 `yaml:"instructions_per_function_std_dev"`
2021-11-24 20:08:47 -08:00
ReturnToBestPeriodMean uint64 `yaml:"return_to_best_period_mean"`
2021-11-23 20:45:03 -08:00
Samples []*Sample `yaml:"samples"`
2021-11-24 19:52:22 -08:00
// Sample indices ranked by each output dimension
SampleRanks [][]int
}
type Score struct {
Current uint64
Total uint64
2021-11-20 18:51:19 -10:00
}
func NewDefinition(r io.Reader) (*Definition, error) {
2021-11-20 18:51:19 -10:00
dec := yaml.NewDecoder(r)
dec.SetStrict(true)
def := &Definition{}
err := dec.Decode(def)
if err != nil {
return nil, err
}
2021-11-24 19:52:22 -08:00
// TODO: Test & handle non-consistent In and Out dimensions
def.buildSampleRanks()
2021-11-20 18:51:19 -10:00
return def, nil
}
2021-11-20 20:17:56 -10:00
func (def *Definition) Grow(statusChan chan<- Status) (*vm.Program, error) {
2021-11-24 19:52:22 -08:00
if statusChan != nil {
defer close(statusChan)
2021-11-20 20:17:56 -10:00
}
2021-11-24 19:52:22 -08:00
status := Status{}
2021-11-20 20:17:56 -10:00
if statusChan != nil {
statusChan <- status
}
prog := &vm.Program{
GlobalMemorySize: def.GlobalMemorySize,
FunctionMemorySize: def.FunctionMemorySize,
InstructionLimit: def.InstructionLimit,
Functions: []*vm.Function{
&vm.Function{},
},
}
2021-11-20 20:17:56 -10:00
for {
status.Attempts++
2021-11-24 20:08:47 -08:00
if def.ReturnToBestPeriodMean != 0 && rand.Uint64()%def.ReturnToBestPeriodMean == 0 {
prog = status.BestProgram.Copy()
}
Mutate(def, prog)
2021-11-20 20:17:56 -10:00
2021-11-24 19:52:22 -08:00
scores, err := def.score(prog)
2021-11-20 20:17:56 -10:00
if err != nil {
// Can never get best score
continue
2021-11-20 20:17:56 -10:00
}
2021-11-24 19:52:22 -08:00
if !def.scoreIsBetter(status.BestScores, scores) {
continue
}
2021-11-23 20:34:10 -08:00
2021-11-24 19:52:22 -08:00
err = def.minifyProgram(prog)
if err != nil {
return nil, err
}
2021-11-23 20:34:10 -08:00
2021-11-24 19:52:22 -08:00
status.BestScores = scores
status.BestProgram = prog.Copy()
2021-11-20 20:17:56 -10:00
2021-11-24 19:52:22 -08:00
if statusChan != nil {
statusChan <- status
}
2021-11-20 20:17:56 -10:00
2021-11-24 19:52:22 -08:00
if status.BestScores[0].Current == status.BestScores[0].Total {
return prog, nil
}
}
}
2021-11-24 19:52:22 -08:00
func (def *Definition) buildSampleRanks() {
for col := 0; col < len(def.Samples[0].Out); col++ {
rank := []int{}
for i := 0; i < len(def.Samples); i++ {
rank = append(rank, i)
2021-11-20 20:17:56 -10:00
}
2021-11-24 19:52:22 -08:00
sort.SliceStable(rank, func(i, j int) bool {
return def.Samples[i].Out[col] < def.Samples[j].Out[col]
})
def.SampleRanks = append(def.SampleRanks, rank)
2021-11-20 20:17:56 -10:00
}
}
2021-11-24 19:52:22 -08:00
func (def *Definition) score(prog *vm.Program) ([]*Score, error) {
outputs := [][]uint64{}
2021-11-20 20:17:56 -10:00
for _, sample := range def.Samples {
state, err := vm.NewState(prog)
if err != nil {
2021-11-24 19:52:22 -08:00
return nil, err
2021-11-20 20:17:56 -10:00
}
sample.SetInputs(state)
err = state.Execute()
if err != nil {
2021-11-24 19:52:22 -08:00
return nil, err
}
2021-11-20 20:17:56 -10:00
2021-11-24 19:52:22 -08:00
output := []uint64{}
for i := 0; i < len(def.Samples[0].Out); i++ {
// TODO: Handle signedness?
output = append(output, state.GlobalMemory().MustReadUnsigned(uint64(i)))
}
outputs = append(outputs, output)
2021-11-20 20:17:56 -10:00
}
2021-11-24 19:52:22 -08:00
// TODO: Points for proximity to target values?
// TODO: Points for correlation coeficient with target values across samples?
return []*Score{
def.scoreMatching(outputs),
def.scoreRank(outputs),
}, nil
2021-11-20 20:17:56 -10:00
}
2021-11-24 19:52:22 -08:00
func (def *Definition) scoreMatching(outputs [][]uint64) *Score {
ret := &Score{}
2021-11-24 19:52:22 -08:00
for s, sample := range def.Samples {
for o, out := range sample.Out {
ret.Total++
if outputs[s][o] == out {
ret.Current++
}
}
}
return ret
}
func (def *Definition) scoreRank(outputs [][]uint64) *Score {
ranks := [][]int{}
for col := 0; col < len(outputs[0]); col++ {
rank := []int{}
for i := 0; i < len(def.Samples); i++ {
rank = append(rank, i)
}
sort.SliceStable(rank, func(i, j int) bool {
return outputs[i][col] < outputs[j][col]
})
ranks = append(ranks, rank)
}
ret := &Score{}
for col, vals := range ranks {
for i, val := range vals {
ret.Total++
if val == def.SampleRanks[col][i] {
ret.Current++
}
}
}
return ret
}
func (def *Definition) scoreIsBetter(old, new []*Score) bool {
if old == nil {
return true
}
for i, score := range new {
best := old[i]
switch {
case score.Current == best.Current:
continue
case score.Current > best.Current:
return true
case score.Current < best.Current:
return false
}
}
2021-11-24 19:52:22 -08:00
// Unchanged
return false
}
2021-11-22 21:34:55 -08:00
func (def *Definition) minifyProgram(prog *vm.Program) error {
for f := 0; f < len(prog.Functions); f++ {
err := def.minifyFunction(prog, f)
if err != nil {
return err
}
}
return nil
}
func (def *Definition) minifyFunction(prog *vm.Program, f int) error {
2021-11-24 19:52:22 -08:00
baseScores, err := def.score(prog)
2021-11-22 21:34:55 -08:00
if err != nil {
return err
}
for loop := true; loop; {
loop = false
for i := 0; i < len(prog.Functions[f].Instructions); i++ {
origInstructions := prog.Functions[f].Instructions
2021-11-22 21:34:55 -08:00
tmp := make([]*vm.Instruction, len(prog.Functions[f].Instructions))
copy(tmp, prog.Functions[f].Instructions)
prog.Functions[f].Instructions = append(tmp[:i], tmp[i+1:]...)
2021-11-24 19:52:22 -08:00
newScores, err := def.score(prog)
// XXX: Use all scores
if err == nil && newScores[0].Current >= baseScores[0].Current {
2021-11-22 21:34:55 -08:00
loop = true
break
} else {
prog.Functions[f].Instructions = origInstructions
}
}
}
return nil
}