mirror of
https://github.com/Oxalide/vsphere-influxdb-go.git
synced 2023-10-10 13:36:51 +02:00
240 lines
4.8 KiB
Go
240 lines
4.8 KiB
Go
// Package neldermead is an implementation of the Nelder-Mead optimization method.
|
|
// Based on work by Michael F. Hutt: http://www.mikehutt.com/neldermead.html
|
|
package neldermead
|
|
|
|
import "math"
|
|
|
|
const (
|
|
defaultMaxIterations = 1000
|
|
// reflection coefficient
|
|
defaultAlpha = 1.0
|
|
// contraction coefficient
|
|
defaultBeta = 0.5
|
|
// expansion coefficient
|
|
defaultGamma = 2.0
|
|
)
|
|
|
|
// Optimizer represents the parameters to the Nelder-Mead simplex method.
|
|
type Optimizer struct {
|
|
// Maximum number of iterations.
|
|
MaxIterations int
|
|
// Reflection coefficient.
|
|
Alpha,
|
|
// Contraction coefficient.
|
|
Beta,
|
|
// Expansion coefficient.
|
|
Gamma float64
|
|
}
|
|
|
|
// New returns a new instance of Optimizer with all values set to the defaults.
|
|
func New() *Optimizer {
|
|
return &Optimizer{
|
|
MaxIterations: defaultMaxIterations,
|
|
Alpha: defaultAlpha,
|
|
Beta: defaultBeta,
|
|
Gamma: defaultGamma,
|
|
}
|
|
}
|
|
|
|
// Optimize applies the Nelder-Mead simplex method with the Optimizer's settings.
|
|
func (o *Optimizer) Optimize(
|
|
objfunc func([]float64) float64,
|
|
start []float64,
|
|
epsilon,
|
|
scale float64,
|
|
) (float64, []float64) {
|
|
n := len(start)
|
|
|
|
//holds vertices of simplex
|
|
v := make([][]float64, n+1)
|
|
for i := range v {
|
|
v[i] = make([]float64, n)
|
|
}
|
|
|
|
//value of function at each vertex
|
|
f := make([]float64, n+1)
|
|
|
|
//reflection - coordinates
|
|
vr := make([]float64, n)
|
|
|
|
//expansion - coordinates
|
|
ve := make([]float64, n)
|
|
|
|
//contraction - coordinates
|
|
vc := make([]float64, n)
|
|
|
|
//centroid - coordinates
|
|
vm := make([]float64, n)
|
|
|
|
// create the initial simplex
|
|
// assume one of the vertices is 0,0
|
|
|
|
pn := scale * (math.Sqrt(float64(n+1)) - 1 + float64(n)) / (float64(n) * math.Sqrt(2))
|
|
qn := scale * (math.Sqrt(float64(n+1)) - 1) / (float64(n) * math.Sqrt(2))
|
|
|
|
for i := 0; i < n; i++ {
|
|
v[0][i] = start[i]
|
|
}
|
|
|
|
for i := 1; i <= n; i++ {
|
|
for j := 0; j < n; j++ {
|
|
if i-1 == j {
|
|
v[i][j] = pn + start[j]
|
|
} else {
|
|
v[i][j] = qn + start[j]
|
|
}
|
|
}
|
|
}
|
|
|
|
// find the initial function values
|
|
for j := 0; j <= n; j++ {
|
|
f[j] = objfunc(v[j])
|
|
}
|
|
|
|
// begin the main loop of the minimization
|
|
for itr := 1; itr <= o.MaxIterations; itr++ {
|
|
|
|
// find the indexes of the largest and smallest values
|
|
vg := 0
|
|
vs := 0
|
|
for i := 0; i <= n; i++ {
|
|
if f[i] > f[vg] {
|
|
vg = i
|
|
}
|
|
if f[i] < f[vs] {
|
|
vs = i
|
|
}
|
|
}
|
|
// find the index of the second largest value
|
|
vh := vs
|
|
for i := 0; i <= n; i++ {
|
|
if f[i] > f[vh] && f[i] < f[vg] {
|
|
vh = i
|
|
}
|
|
}
|
|
|
|
// calculate the centroid
|
|
for i := 0; i <= n-1; i++ {
|
|
cent := 0.0
|
|
for m := 0; m <= n; m++ {
|
|
if m != vg {
|
|
cent += v[m][i]
|
|
}
|
|
}
|
|
vm[i] = cent / float64(n)
|
|
}
|
|
|
|
// reflect vg to new vertex vr
|
|
for i := 0; i <= n-1; i++ {
|
|
vr[i] = vm[i] + o.Alpha*(vm[i]-v[vg][i])
|
|
}
|
|
|
|
// value of function at reflection point
|
|
fr := objfunc(vr)
|
|
|
|
if fr < f[vh] && fr >= f[vs] {
|
|
for i := 0; i <= n-1; i++ {
|
|
v[vg][i] = vr[i]
|
|
}
|
|
f[vg] = fr
|
|
}
|
|
|
|
// investigate a step further in this direction
|
|
if fr < f[vs] {
|
|
for i := 0; i <= n-1; i++ {
|
|
ve[i] = vm[i] + o.Gamma*(vr[i]-vm[i])
|
|
}
|
|
|
|
// value of function at expansion point
|
|
fe := objfunc(ve)
|
|
|
|
// by making fe < fr as opposed to fe < f[vs],
|
|
// Rosenbrocks function takes 63 iterations as opposed
|
|
// to 64 when using double variables.
|
|
|
|
if fe < fr {
|
|
for i := 0; i <= n-1; i++ {
|
|
v[vg][i] = ve[i]
|
|
}
|
|
f[vg] = fe
|
|
} else {
|
|
for i := 0; i <= n-1; i++ {
|
|
v[vg][i] = vr[i]
|
|
}
|
|
f[vg] = fr
|
|
}
|
|
}
|
|
|
|
// check to see if a contraction is necessary
|
|
if fr >= f[vh] {
|
|
if fr < f[vg] && fr >= f[vh] {
|
|
// perform outside contraction
|
|
for i := 0; i <= n-1; i++ {
|
|
vc[i] = vm[i] + o.Beta*(vr[i]-vm[i])
|
|
}
|
|
} else {
|
|
// perform inside contraction
|
|
for i := 0; i <= n-1; i++ {
|
|
vc[i] = vm[i] - o.Beta*(vm[i]-v[vg][i])
|
|
}
|
|
}
|
|
|
|
// value of function at contraction point
|
|
fc := objfunc(vc)
|
|
|
|
if fc < f[vg] {
|
|
for i := 0; i <= n-1; i++ {
|
|
v[vg][i] = vc[i]
|
|
}
|
|
f[vg] = fc
|
|
} else {
|
|
// at this point the contraction is not successful,
|
|
// we must halve the distance from vs to all the
|
|
// vertices of the simplex and then continue.
|
|
|
|
for row := 0; row <= n; row++ {
|
|
if row != vs {
|
|
for i := 0; i <= n-1; i++ {
|
|
v[row][i] = v[vs][i] + (v[row][i]-v[vs][i])/2.0
|
|
}
|
|
}
|
|
}
|
|
f[vg] = objfunc(v[vg])
|
|
f[vh] = objfunc(v[vh])
|
|
}
|
|
}
|
|
|
|
// test for convergence
|
|
fsum := 0.0
|
|
for i := 0; i <= n; i++ {
|
|
fsum += f[i]
|
|
}
|
|
favg := fsum / float64(n+1)
|
|
s := 0.0
|
|
for i := 0; i <= n; i++ {
|
|
s += math.Pow((f[i]-favg), 2.0) / float64(n)
|
|
}
|
|
s = math.Sqrt(s)
|
|
if s < epsilon {
|
|
break
|
|
}
|
|
}
|
|
|
|
// find the index of the smallest value
|
|
vs := 0
|
|
for i := 0; i <= n; i++ {
|
|
if f[i] < f[vs] {
|
|
vs = i
|
|
}
|
|
}
|
|
|
|
parameters := make([]float64, n)
|
|
for i := 0; i < n; i++ {
|
|
parameters[i] = v[vs][i]
|
|
}
|
|
|
|
min := objfunc(v[vs])
|
|
|
|
return min, parameters
|
|
}
|