math-optim/algo/algo.go
surtur 805db5088b
All checks were successful
continuous-integration/drone/push Build is passing
go(algo): close chans after use
2022-08-05 17:23:10 +02:00

253 lines
5.9 KiB
Go

// Copyright 2022 wanderer <a_mirre at utb dot cz>
// SPDX-License-Identifier: GPL-3.0-or-later
package algo
import (
"fmt"
"log"
"sort"
"sync"
"git.dotya.ml/wanderer/math-optim/bench"
"git.dotya.ml/wanderer/math-optim/report"
"git.dotya.ml/wanderer/math-optim/stats"
)
// mu protects access to meanStats.
var mu sync.Mutex
// mCoMPL protexts access to comparisonOfMeansPicList.
var mCoMPL sync.Mutex
var meanStats = &stats.MeanStats{}
var comparisonOfMeansPicList = &report.PicList{Algo: "Comparison of Means"}
// getComparisonOfMeansPics returns a sorted slice of pics field from the
// package global 'algoMeanPics'.
func getComparisonOfMeansPics() []report.Pic {
// note: sorting by filename (dimens value being 0-padded at generation
// time), relying on this as a hack so that we didn't have to implement our
// own natural-order sorter.
sort.Sort(comparisonOfMeansPicList.Pics)
return comparisonOfMeansPicList.Pics
}
// GetMeanStats returns a pointer of type stats.MeanStats to a sorted package
// global 'meanStats'.
func GetMeanStats() *stats.MeanStats {
sort.Sort(meanStats)
return meanStats
}
func PrepComparisonOfMeans(wg *sync.WaitGroup) (*report.PicList, int) {
pL := report.NewPicList()
meanStats := GetMeanStats()
algos := make([]string, 0)
// learn how many algos were processed based on the data.
for _, v := range meanStats.AlgoMeans {
// if algos is empty just add the value directly, else determine if
// it's already been added or not.
if len(algos) > 0 {
alreadyadded := false
for _, algoName := range algos {
if algoName == v.Algo {
// early bail if already added.
alreadyadded = true
break
}
}
if !alreadyadded {
algos = append(algos, v.Algo)
}
} else {
algos = append(algos, v.Algo)
}
}
// construct title consisting of names of all involved algorithms.
for _, v := range algos {
switch pL.Algo {
case "":
pL.Algo = v
default:
pL.Algo += " vs " + v
}
}
log.Println(`generating "Comparison of Means" plots`)
algoCount := len(algos)
dimLen := len(bench.Dimensions)
benchCount := len(bench.Functions)
// note: this is a wee bit ugly.
for d := 0; d < dimLen; d++ {
for i := 0; i < benchCount; i++ {
dimXAlgoMeanVals := make([]stats.AlgoMeanVals, 0, algoCount)
for j := 0; j < algoCount*benchCount; j += benchCount {
neighbInfo := ""
// only add info about neighbours if it was changed (from
// the default of -1).
if n := meanStats.AlgoMeans[d+j].BenchMeans[i].Neighbours; n != -1 {
neighbInfo = fmt.Sprintf(" (N: %d)", n)
}
ms := &stats.AlgoMeanVals{
Title: meanStats.AlgoMeans[d+j].Algo + neighbInfo,
MeanVals: meanStats.AlgoMeans[d+j].BenchMeans[i].MeanVals,
}
dimXAlgoMeanVals = append(dimXAlgoMeanVals, *ms)
}
dimens := meanStats.AlgoMeans[d].BenchMeans[i].Dimens
iterations := meanStats.AlgoMeans[d].BenchMeans[i].Iterations
bench := meanStats.AlgoMeans[d].BenchMeans[i].Bench
wg.Add(1)
// construct plots concurrently.
go PlotMeanValsMulti(
wg, dimens, iterations, bench, "plot-", ".svg",
dimXAlgoMeanVals...,
)
}
}
// wait for all the plotting to conclude.
wg.Wait()
pL.Pics = getComparisonOfMeansPics()
return pL, benchCount
}
// DoRandomSearch executes a search using the 'Random search' method.
func DoRandomSearch(wg *sync.WaitGroup, m *sync.Mutex) {
defer wg.Done()
printRandomSearch("starting...")
// funcCount is the number of bench functions available.
funcCount := len(bench.Functions)
// stats for the current algo (RandomSearch).
algoStats := make([][]stats.Stats, funcCount)
// ch serves as a way to get the actual computed output.
ch := make(chan []stats.Stats, funcCount)
defer close(ch)
for i := range algoStats {
// ng y'all.
go RandomSearchNG(10000, 30, bench.Dimensions, bench.FuncNames[i], ch)
}
// get results.
for i := range algoStats {
s := <-ch
algoStats[i] = s
}
pCh := make(chan report.PicList, funcCount*len(bench.Dimensions))
pMeanCh := make(chan report.PicList, funcCount*len(bench.Dimensions))
defer close(pCh)
defer close(pMeanCh)
for i := range algoStats {
go plotAllDims(algoStats[i], "plot", ".svg", pCh, pMeanCh)
}
pLs := []report.PicList{}
pLsMean := []report.PicList{}
for range algoStats {
pL := <-pCh
pLMean := <-pMeanCh
pLs = append(pLs, pL)
pLsMean = append(pLsMean, pLMean)
}
algoName := "Random Search"
// protect access to shared data.
m.Lock()
report.SavePicsToFile(pLs, pLsMean, algoName)
stats.SaveTable(algoName, algoStats)
m.Unlock()
}
// DoStochasticHillClimbing performs a search using the 'Stochastic Hill
// Climbing' method.
func DoStochasticHillClimbing(wg *sync.WaitGroup, m *sync.Mutex) {
defer wg.Done()
printSHC("starting...")
// funcCount is the number of bench functions available.
funcCount := len(bench.Functions)
// stats for the current algo (StochasticHillClimber).
algoStats := make([][]stats.Stats, funcCount)
// ch serves as a way to get the actual computed output.
ch := make(chan []stats.Stats, funcCount)
defer close(ch)
for i := range algoStats {
// params:
// maxFES, benchMinIters, neighbours int,
// theD []int,
// benchFunc string,
// ch chan []stats.Stats
go HillClimb(10000, 30, 10, bench.Dimensions, bench.FuncNames[i], ch)
}
// get results.
for i := range algoStats {
s := <-ch
algoStats[i] = s
}
pCh := make(chan report.PicList, funcCount*len(bench.Dimensions))
pMeanCh := make(chan report.PicList, funcCount*len(bench.Dimensions))
defer close(pCh)
defer close(pMeanCh)
for _, algoStat := range algoStats {
go plotAllDims(algoStat, "plot", ".svg", pCh, pMeanCh)
}
pLs := []report.PicList{}
pLsMean := []report.PicList{}
for range algoStats {
pL := <-pCh
pLMean := <-pMeanCh
pLs = append(pLs, pL)
pLsMean = append(pLsMean, pLMean)
}
algoName := "Stochastic Hill Climbing"
// protect access to shared data.
m.Lock()
report.SavePicsToFile(pLs, pLsMean, algoName)
stats.SaveTable(algoName, algoStats)
m.Unlock()
}