math-optim/algo/de/jDE.go
leo 2216b93426
All checks were successful
continuous-integration/drone/push Build is passing
go(de): add Run(), unexport Evolve()
* use evolve()
* add a way to calculate GAMaxFES (MaxFES for Genetic Algorithms) in the
  bench package
2023-01-19 19:55:41 +01:00

166 lines
4.7 KiB
Go

// Copyright 2023 wanderer <a_mirre at utb dot cz>
// SPDX-License-Identifier: GPL-3.0-or-later
package de
import (
"log"
"os"
"sync"
"git.dotya.ml/wanderer/math-optim/bench"
"git.dotya.ml/wanderer/math-optim/stats"
)
// JDE is a holder for the settings of an instance of a self-adapting
// differential evolution (jDE) algorithm.
type JDE struct {
// Generations denotes the number of generations the population evolves
// for. Special value -1 disables limiting the number of generations.
Generations int
// Dimensions to solve the problem for.
Dimensions []int
// F is the differential weight (mutation/weighting factor).
F float64
// CR is the crossover probability constant.
CR float64
// MutationStrategy selects the mutation strategy, i.e. the variant of the
// jDE algorithm (0..17), see mutationStrategies.go for more details.
MutationStrategy int
// AdptScheme is the parameter self-adaptation scheme (0..1).
AdptScheme int
// NP is the initial population size.
NP int
// Population is a pointer to population
Population *Population
// BenchName is a name of the problem to optimise.
BenchName string
// initialised denotes the initialisation state of the struct.
initialised bool
}
const (
// jDEMinNP is the minimum size of the initial population for jDE.
jDEMinNP = 4
// fMin is the minimum allowed value of the differential weight.
fMin = 0.5
// fMax is the maximum allowed value of the differential weight.
fMax = 2.0
// crMin is the minimum allowed value of the crossover probability constant.
crMin = 0.2
// crMax is the maximum allowed value of the crossover probability constant.
crMax = 0.9
)
// jDELogger declares and initialises a "custom" jDE logger.
var jDELogger = log.New(os.Stderr, " *** δ jDE:", log.Ldate|log.Ltime|log.Lshortfile)
// Init initialises the jDE algorithm, performs sanity checks on the inputs.
func (j *JDE) Init(generations, mutStrategy, adptScheme, np int, f, cr float64, dimensions []int, bench string, ch chan []stats.Stats) {
if j == nil {
jDELogger.Fatalln("jDE needs to be initialised before calling RunjDE, exiting...")
}
// check input parameters.
switch {
case generations == 0:
jDELogger.Fatalln("Generations cannot be 0, got", generations)
case generations == -1:
jDELogger.Println("Generations is '-1', disabling generation limits..")
case mutStrategy < 0 || mutStrategy > 17:
jDELogger.Fatalln("Mutation strategy needs to be from the interval <0; 17>, got", mutStrategy)
case adptScheme < 0 || adptScheme > 1:
jDELogger.Fatalln("Parameter self-adaptation scheme needs to be from the interval <0; 1>, got", adptScheme)
case np < jDEMinNP:
jDELogger.Fatalf("NP needs to be greater than %d, got: %d\n.", jDEMinNP, np)
case f < fMin || f > fMax:
jDELogger.Fatalf("F needs to be from the interval <%f;%f>, got: %f\n.", fMin, fMax, f)
case cr < crMin || cr > crMax:
jDELogger.Fatalf("CR needs to be from the interval <%f;>%f, got: %f\n.", crMin, crMax, cr)
case len(dimensions) == 0:
jDELogger.Fatalf("Dimensions cannot be empty, got: %+v\n", dimensions)
case bench == "":
jDELogger.Fatalln("Bench cannot be empty, got:", bench)
}
j.Generations = generations
j.MutationStrategy = mutStrategy
j.AdptScheme = adptScheme
j.NP = np
j.F = f
j.CR = cr
j.Dimensions = dimensions
j.BenchName = bench
j.initialised = true
pop := newPopulation(bench, j.NP)
pop.Init()
}
// Run self-adapting differential evolution algorithm.
func (j *JDE) Run() {
if j == nil {
jDELogger.Fatalln("jDE is nil, NewjDE() needs to be called first. exiting...")
}
if !j.initialised {
jDELogger.Fatalln("jDE needs to be initialised before calling Run(), exiting...")
}
// have a wait group.
var wg sync.WaitGroup
// we're be spawning goroutines per dimension and that is the number of
// goroutines we need to wait for.
wg.Add(len(j.Dimensions))
// run Evolve for for all dimensions.
for _, dim := range j.Dimensions {
maxFES := bench.GetGAMaxFES(dim)
j.evolve(maxFES, &wg)
}
// wait for all.
wg.Wait()
}
// evolve evolves a population by running the jDE (self-adapting Differential
// Evolution) algorithm on the passed population until termination conditions
// are met.
func (j *JDE) evolve(maxFES int, wg *sync.WaitGroup) {}
// NewjDE returns a pointer to a new, uninitialised jDE instance.
func NewjDE() *JDE {
return &JDE{}
}
// LogPrintln wraps the jDE logger's Println func.
func LogPrintln(v ...any) {
jDELogger.Println(v...)
}
// LogPrintf wraps the jDE logger's Printf func.
func LogPrintf(s string, v ...any) {
jDELogger.Printf(s, v...)
}
// LogFatalln wraps the jDE logger's Fatalln func.
func LogFatalln(s string) {
jDELogger.Fatalln(s)
}
// LogFatalf wraps the jDE logger's Fatalf func.
func LogFatalf(s string, v ...any) {
jDELogger.Fatalf(s, v...)
}