github.com/influxdata/influxdb/v2@v2.7.6/influxql/query/neldermead/neldermead.go (about)

     1  // Package neldermead is an implementation of the Nelder-Mead optimization method.
     2  // Based on work by Michael F. Hutt: http://www.mikehutt.com/neldermead.html
     3  package neldermead
     4  
     5  import "math"
     6  
     7  const (
     8  	defaultMaxIterations = 1000
     9  	// reflection coefficient
    10  	defaultAlpha = 1.0
    11  	// contraction coefficient
    12  	defaultBeta = 0.5
    13  	// expansion coefficient
    14  	defaultGamma = 2.0
    15  )
    16  
    17  // Optimizer represents the parameters to the Nelder-Mead simplex method.
    18  type Optimizer struct {
    19  	// Maximum number of iterations.
    20  	MaxIterations int
    21  	// Reflection coefficient.
    22  	Alpha,
    23  	// Contraction coefficient.
    24  	Beta,
    25  	// Expansion coefficient.
    26  	Gamma float64
    27  }
    28  
    29  // New returns a new instance of Optimizer with all values set to the defaults.
    30  func New() *Optimizer {
    31  	return &Optimizer{
    32  		MaxIterations: defaultMaxIterations,
    33  		Alpha:         defaultAlpha,
    34  		Beta:          defaultBeta,
    35  		Gamma:         defaultGamma,
    36  	}
    37  }
    38  
    39  // Optimize applies the Nelder-Mead simplex method with the Optimizer's settings.
    40  func (o *Optimizer) Optimize(
    41  	objfunc func([]float64) float64,
    42  	start []float64,
    43  	epsilon,
    44  	scale float64,
    45  ) (float64, []float64) {
    46  	n := len(start)
    47  
    48  	//holds vertices of simplex
    49  	v := make([][]float64, n+1)
    50  	for i := range v {
    51  		v[i] = make([]float64, n)
    52  	}
    53  
    54  	//value of function at each vertex
    55  	f := make([]float64, n+1)
    56  
    57  	//reflection - coordinates
    58  	vr := make([]float64, n)
    59  
    60  	//expansion - coordinates
    61  	ve := make([]float64, n)
    62  
    63  	//contraction - coordinates
    64  	vc := make([]float64, n)
    65  
    66  	//centroid - coordinates
    67  	vm := make([]float64, n)
    68  
    69  	// create the initial simplex
    70  	// assume one of the vertices is 0,0
    71  
    72  	pn := scale * (math.Sqrt(float64(n+1)) - 1 + float64(n)) / (float64(n) * math.Sqrt(2))
    73  	qn := scale * (math.Sqrt(float64(n+1)) - 1) / (float64(n) * math.Sqrt(2))
    74  
    75  	for i := 0; i < n; i++ {
    76  		v[0][i] = start[i]
    77  	}
    78  
    79  	for i := 1; i <= n; i++ {
    80  		for j := 0; j < n; j++ {
    81  			if i-1 == j {
    82  				v[i][j] = pn + start[j]
    83  			} else {
    84  				v[i][j] = qn + start[j]
    85  			}
    86  		}
    87  	}
    88  
    89  	// find the initial function values
    90  	for j := 0; j <= n; j++ {
    91  		f[j] = objfunc(v[j])
    92  	}
    93  
    94  	// begin the main loop of the minimization
    95  	for itr := 1; itr <= o.MaxIterations; itr++ {
    96  
    97  		// find the indexes of the largest and smallest values
    98  		vg := 0
    99  		vs := 0
   100  		for i := 0; i <= n; i++ {
   101  			if f[i] > f[vg] {
   102  				vg = i
   103  			}
   104  			if f[i] < f[vs] {
   105  				vs = i
   106  			}
   107  		}
   108  		// find the index of the second largest value
   109  		vh := vs
   110  		for i := 0; i <= n; i++ {
   111  			if f[i] > f[vh] && f[i] < f[vg] {
   112  				vh = i
   113  			}
   114  		}
   115  
   116  		// calculate the centroid
   117  		for i := 0; i <= n-1; i++ {
   118  			cent := 0.0
   119  			for m := 0; m <= n; m++ {
   120  				if m != vg {
   121  					cent += v[m][i]
   122  				}
   123  			}
   124  			vm[i] = cent / float64(n)
   125  		}
   126  
   127  		// reflect vg to new vertex vr
   128  		for i := 0; i <= n-1; i++ {
   129  			vr[i] = vm[i] + o.Alpha*(vm[i]-v[vg][i])
   130  		}
   131  
   132  		// value of function at reflection point
   133  		fr := objfunc(vr)
   134  
   135  		if fr < f[vh] && fr >= f[vs] {
   136  			for i := 0; i <= n-1; i++ {
   137  				v[vg][i] = vr[i]
   138  			}
   139  			f[vg] = fr
   140  		}
   141  
   142  		// investigate a step further in this direction
   143  		if fr < f[vs] {
   144  			for i := 0; i <= n-1; i++ {
   145  				ve[i] = vm[i] + o.Gamma*(vr[i]-vm[i])
   146  			}
   147  
   148  			// value of function at expansion point
   149  			fe := objfunc(ve)
   150  
   151  			// by making fe < fr as opposed to fe < f[vs],
   152  			// Rosenbrocks function takes 63 iterations as opposed
   153  			// to 64 when using double variables.
   154  
   155  			if fe < fr {
   156  				for i := 0; i <= n-1; i++ {
   157  					v[vg][i] = ve[i]
   158  				}
   159  				f[vg] = fe
   160  			} else {
   161  				for i := 0; i <= n-1; i++ {
   162  					v[vg][i] = vr[i]
   163  				}
   164  				f[vg] = fr
   165  			}
   166  		}
   167  
   168  		// check to see if a contraction is necessary
   169  		if fr >= f[vh] {
   170  			if fr < f[vg] && fr >= f[vh] {
   171  				// perform outside contraction
   172  				for i := 0; i <= n-1; i++ {
   173  					vc[i] = vm[i] + o.Beta*(vr[i]-vm[i])
   174  				}
   175  			} else {
   176  				// perform inside contraction
   177  				for i := 0; i <= n-1; i++ {
   178  					vc[i] = vm[i] - o.Beta*(vm[i]-v[vg][i])
   179  				}
   180  			}
   181  
   182  			// value of function at contraction point
   183  			fc := objfunc(vc)
   184  
   185  			if fc < f[vg] {
   186  				for i := 0; i <= n-1; i++ {
   187  					v[vg][i] = vc[i]
   188  				}
   189  				f[vg] = fc
   190  			} else {
   191  				// at this point the contraction is not successful,
   192  				// we must halve the distance from vs to all the
   193  				// vertices of the simplex and then continue.
   194  
   195  				for row := 0; row <= n; row++ {
   196  					if row != vs {
   197  						for i := 0; i <= n-1; i++ {
   198  							v[row][i] = v[vs][i] + (v[row][i]-v[vs][i])/2.0
   199  						}
   200  					}
   201  				}
   202  				f[vg] = objfunc(v[vg])
   203  				f[vh] = objfunc(v[vh])
   204  			}
   205  		}
   206  
   207  		// test for convergence
   208  		fsum := 0.0
   209  		for i := 0; i <= n; i++ {
   210  			fsum += f[i]
   211  		}
   212  		favg := fsum / float64(n+1)
   213  		s := 0.0
   214  		for i := 0; i <= n; i++ {
   215  			s += math.Pow((f[i]-favg), 2.0) / float64(n)
   216  		}
   217  		s = math.Sqrt(s)
   218  		if s < epsilon {
   219  			break
   220  		}
   221  	}
   222  
   223  	// find the index of the smallest value
   224  	vs := 0
   225  	for i := 0; i <= n; i++ {
   226  		if f[i] < f[vs] {
   227  			vs = i
   228  		}
   229  	}
   230  
   231  	parameters := make([]float64, n)
   232  	for i := 0; i < n; i++ {
   233  		parameters[i] = v[vs][i]
   234  	}
   235  
   236  	min := objfunc(v[vs])
   237  
   238  	return min, parameters
   239  }