248 lines
6.3 KiB
Go
248 lines
6.3 KiB
Go
// Copyright 2022 wanderer <a_mirre at utb dot cz>
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package algo
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
|
|
"git.dotya.ml/wanderer/math-optim/bench"
|
|
"git.dotya.ml/wanderer/math-optim/stats"
|
|
"gonum.org/v1/gonum/stat/distuv"
|
|
)
|
|
|
|
func getRandomSearchLogPrefix() string {
|
|
return " *** random search:"
|
|
}
|
|
|
|
func fmtRandomSearchOut(input string) string {
|
|
return getRandomSearchLogPrefix() + " " + input
|
|
}
|
|
|
|
func printRandomSearch(input string) {
|
|
if _, err := fmt.Fprintln(os.Stderr, fmtRandomSearchOut(input)); err != nil {
|
|
fmt.Fprintf(
|
|
os.Stdout,
|
|
getRandomSearchLogPrefix(),
|
|
"error while printing to stderr: %q\n * original message was: %q",
|
|
err, input,
|
|
)
|
|
}
|
|
}
|
|
|
|
func genValsRandomSearch(dimens uint, vals []float64, uniform *distuv.Uniform) {
|
|
for i := uint(0); i < dimens; i++ {
|
|
// using Uniform.Rand from gonum's stat/distuv package.
|
|
// https://pkg.go.dev/gonum.org/v1/gonum/stat/distuv#Uniform.Rand
|
|
// boundaries are already set at this point.
|
|
vals[i] = uniform.Rand()
|
|
}
|
|
}
|
|
|
|
// singleRandomSearch performs a single iteration of the 'RandomSearch' algorithm.
|
|
// it takes a couple of arguments:
|
|
// * dimens uint: number of dimensions of the objective function
|
|
// * f func([]float64) float64: bench func to execute (see Functions map in
|
|
// bench/functions.go)
|
|
// * min/max float64: the upper/lower limit of the uniform distribution span,
|
|
// which is relevant to the objective function.
|
|
func singleRandomSearch(dimens uint, f func([]float64) float64, min, max float64) ([]float64, float64) {
|
|
vals := make([]float64, dimens)
|
|
|
|
// create a continuous uniform distribution representation within min/max bounds
|
|
uniformDist := distuv.Uniform{Min: min, Max: max}
|
|
|
|
genValsRandomSearch(dimens, vals, &uniformDist)
|
|
|
|
// result of the bench function.
|
|
res := f(vals)
|
|
|
|
return vals, res
|
|
}
|
|
|
|
// TODO(me): split this up
|
|
// nolint: gocognit
|
|
func RandomSearch(fes uint) {
|
|
if fes == 0 {
|
|
log.Fatalln(" random search: fes set to 0, bailing")
|
|
}
|
|
|
|
picPrefix := "res/"
|
|
|
|
var valsSchwefel []Values
|
|
|
|
var valsDeJong1st []Values
|
|
|
|
var valsDeJong2nd []Values
|
|
|
|
var bestResultsSchwefel []float64
|
|
|
|
var bestResultsDeJong1st []float64
|
|
|
|
var bestResultsDeJong2nd []float64
|
|
|
|
// randomSearchStats stores Stats for all the function/dimension
|
|
// combinations for this algo.
|
|
var randomSearchStats []stats.Stats
|
|
|
|
// iterations needed to establish a minimal viable statistical baseline
|
|
minIters := 30
|
|
|
|
//nolint: staticcheck
|
|
for _, dimens := range bench.Dimensions {
|
|
randomSearchStatXD := &stats.Stats{
|
|
Algo: "Random Search",
|
|
Dimens: int(dimens),
|
|
Iterations: minIters,
|
|
Generations: bench.MaxFES,
|
|
}
|
|
schwefelStats := &stats.FuncStats{BenchName: "Schwefel"}
|
|
deJong1stStats := &stats.FuncStats{BenchName: "De Jong 1st"}
|
|
deJong2ndStats := &stats.FuncStats{BenchName: "De Jong 2nd"}
|
|
|
|
randomSearchStatXD.Dimens = int(dimens)
|
|
|
|
var resultsSchwefel Values
|
|
|
|
var resultsDeJong1st Values
|
|
|
|
var resultsDeJong2nd Values
|
|
|
|
log.Println("dimens:", dimens)
|
|
|
|
// current iteration is named iter
|
|
for iter := 0; iter < minIters; iter++ {
|
|
if iter == 29 {
|
|
log.Println("iter:", iter, "(last for dimen)")
|
|
}
|
|
|
|
var bestResult float64
|
|
|
|
for j := 0; j < int(fes); j++ {
|
|
// run Schwefel.
|
|
v, r := singleRandomSearch(
|
|
dimens,
|
|
bench.Functions["Schwefel"],
|
|
bench.SchwefelParams.Min(),
|
|
bench.SchwefelParams.Max(),
|
|
)
|
|
valsSchwefel = append(valsSchwefel, v)
|
|
resultsSchwefel = append(resultsSchwefel, r)
|
|
|
|
// collect results after each call to bench function.
|
|
schwefelStats.Solution = append(
|
|
schwefelStats.Solution,
|
|
stats.BenchRound{Iteration: iter, Results: resultsSchwefel},
|
|
)
|
|
|
|
switch j {
|
|
// first iteration
|
|
case 0:
|
|
bestResult = r
|
|
default:
|
|
// any other than the first iteration and a better solution
|
|
if r < bestResult {
|
|
bestResult = r
|
|
}
|
|
}
|
|
|
|
bestResultsSchwefel = append(bestResultsSchwefel, bestResult)
|
|
}
|
|
|
|
// add schwefel results (for the current dimension) to the stats
|
|
// slice.
|
|
randomSearchStatXD.BenchFuncStats = append(
|
|
randomSearchStatXD.BenchFuncStats,
|
|
*schwefelStats,
|
|
)
|
|
|
|
for k := 0; k < int(fes); k++ {
|
|
// run De Jong 1st.
|
|
v, r := singleRandomSearch(
|
|
dimens,
|
|
bench.Functions["De Jong 1st"],
|
|
bench.DeJong1Params.Min(),
|
|
bench.DeJong1Params.Max(),
|
|
)
|
|
valsDeJong1st = append(valsDeJong1st, v)
|
|
resultsDeJong1st = append(resultsDeJong1st, r)
|
|
|
|
// collect results after each call to bench function.
|
|
deJong1stStats.Solution = append(
|
|
deJong1stStats.Solution,
|
|
stats.BenchRound{Iteration: iter, Results: resultsDeJong1st},
|
|
)
|
|
|
|
// first iteration or better solution
|
|
if k == 0 || r < bestResult {
|
|
bestResult = r
|
|
}
|
|
|
|
bestResultsDeJong1st = append(bestResultsDeJong1st, bestResult)
|
|
}
|
|
|
|
// add De Jong 1st results (for the current dimension) to the stats
|
|
// slice.
|
|
randomSearchStatXD.BenchFuncStats = append(
|
|
randomSearchStatXD.BenchFuncStats,
|
|
*deJong1stStats,
|
|
)
|
|
|
|
for l := 0; l < int(fes); l++ {
|
|
// run De Jong 2nd.
|
|
v, r := singleRandomSearch(
|
|
dimens,
|
|
bench.Functions["De Jong 2nd"],
|
|
bench.DeJong2Params.Min(),
|
|
bench.DeJong2Params.Max(),
|
|
)
|
|
valsDeJong2nd = append(valsDeJong2nd, v)
|
|
resultsDeJong2nd = append(resultsDeJong2nd, r)
|
|
|
|
// collect results after each call to bench function.
|
|
deJong2ndStats.Solution = append(
|
|
deJong2ndStats.Solution,
|
|
stats.BenchRound{Iteration: iter, Results: resultsDeJong2nd},
|
|
)
|
|
|
|
// first iteration or better solution
|
|
if l == 0 || r < bestResult {
|
|
bestResult = r
|
|
}
|
|
|
|
bestResultsDeJong2nd = append(bestResultsDeJong2nd, bestResult)
|
|
}
|
|
|
|
// add De Jong 2nd results (for the current dimension) to the stats
|
|
// slice.
|
|
randomSearchStatXD.BenchFuncStats = append(
|
|
randomSearchStatXD.BenchFuncStats,
|
|
*deJong2ndStats,
|
|
)
|
|
|
|
// append the stats slice for the current dimension to the (per
|
|
// algo) stats slice.
|
|
randomSearchStats = append(randomSearchStats, *randomSearchStatXD)
|
|
|
|
if iter == 29 {
|
|
log.Println("do plot")
|
|
doCombined30Plot(
|
|
schwefelStats,
|
|
dimens,
|
|
picPrefix+"schw-"+fmt.Sprint(dimens)+"D.svg",
|
|
)
|
|
doCombined30Plot(deJong1stStats,
|
|
dimens,
|
|
picPrefix+"dj1-"+fmt.Sprint(dimens)+"D.svg",
|
|
)
|
|
doCombined30Plot(deJong2ndStats,
|
|
dimens,
|
|
picPrefix+"dj2-"+fmt.Sprint(dimens)+"D.svg",
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|