vsphere-influxdb-go/vendor/github.com/influxdata/influxdb/influxql/neldermead/neldermead.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
}