195 lines
5.7 KiB
Go
195 lines
5.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"
|
|
"time"
|
|
|
|
"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
|
|
// BenchMinIters is the number of iterations the bench function will be re-run (for statistical purposes).
|
|
BenchMinIters 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
|
|
// fVect holds the F values in a slice.
|
|
fVect []float64
|
|
// crVect holds the CR values in a slice.
|
|
crVect []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
|
|
// BenchName is a name of the problem to optimise.
|
|
BenchName string
|
|
// ch is a channel for writing back computed results.
|
|
ch chan []stats.Stats
|
|
// 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, benchMinIters, 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 benchMinIters < 1:
|
|
jDELogger.Fatalln("Minimum bench iterations cannot be less than 1, got:", benchMinIters)
|
|
|
|
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 cannot be less 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.BenchMinIters = benchMinIters
|
|
j.MutationStrategy = mutStrategy
|
|
j.AdptScheme = adptScheme
|
|
j.NP = np
|
|
j.F = f
|
|
j.CR = cr
|
|
j.Dimensions = dimensions
|
|
j.BenchName = bench
|
|
j.ch = ch
|
|
|
|
fV := make([]float64, np)
|
|
crV := make([]float64, np)
|
|
|
|
for i := 0; i < np; i++ {
|
|
fV[i] = f
|
|
crV[i] = cr
|
|
}
|
|
|
|
j.fVect = fV
|
|
j.crVect = crV
|
|
|
|
j.initialised = true
|
|
}
|
|
|
|
// InitAndRun initialises the jDE algorithm, performs sanity checks on the
|
|
// inputs and calls the Run method.
|
|
func (j *JDE) InitAndRun(generations, benchMinIters, mutStrategy, adptScheme, np int, f, cr float64, dimensions []int, bench string, ch chan []stats.Stats) {
|
|
if j == nil {
|
|
jDELogger.Fatalln("jDE is nil, NewjDE() needs to be called first. exiting...")
|
|
}
|
|
|
|
j.Init(generations, benchMinIters, mutStrategy, adptScheme, np, f, cr, dimensions, bench, ch)
|
|
|
|
j.Run()
|
|
}
|
|
|
|
// 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...")
|
|
}
|
|
|
|
// run evolve for for all dimensions.
|
|
for _, dim := range j.Dimensions {
|
|
maxFES := bench.GetGAMaxFES(dim)
|
|
|
|
// create a population with known params.
|
|
pop := newPopulation(j.BenchName, j.NP, dim)
|
|
|
|
// set population seed.
|
|
pop.Seed = uint64(time.Now().UnixNano())
|
|
// initialise the population.
|
|
pop.Init()
|
|
|
|
j.evolve(maxFES, pop)
|
|
}
|
|
}
|
|
|
|
// 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, pop *Population) {}
|
|
|
|
// 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...)
|
|
}
|