// Copyright ©2018 The Gonum Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package optimize import ( "math" "gonum.org/v1/gonum/mat" ) var _ Method = (*ListSearch)(nil) // ListSearch finds the optimum location from a specified list of possible // optimum locations. type ListSearch struct { // Locs is the list of locations to optimize. Each row of Locs is a location // to optimize. The number of columns of Locs must match the dimensions // passed to InitGlobal, and Locs must have at least one row. Locs mat.Matrix eval int rows int bestF float64 bestIdx int } func (*ListSearch) Uses(has Available) (uses Available, err error) { return has.function() } // Init initializes the method for optimization. The input dimension // must match the number of columns of Locs. func (l *ListSearch) Init(dim, tasks int) int { if dim <= 0 { panic(nonpositiveDimension) } if tasks < 0 { panic(negativeTasks) } r, c := l.Locs.Dims() if r == 0 { panic("listsearch: list matrix has no rows") } if c != dim { panic("listsearch: supplied dimension does not match list columns") } l.eval = 0 l.rows = r l.bestF = math.Inf(1) l.bestIdx = -1 return min(r, tasks) } func (l *ListSearch) sendNewLoc(operation chan<- Task, task Task) { task.Op = FuncEvaluation task.ID = l.eval mat.Row(task.X, l.eval, l.Locs) l.eval++ operation <- task } func (l *ListSearch) updateMajor(operation chan<- Task, task Task) { // Update the best value seen so far, and send a MajorIteration. if task.F < l.bestF { l.bestF = task.F l.bestIdx = task.ID } else { task.F = l.bestF mat.Row(task.X, l.bestIdx, l.Locs) } task.Op = MajorIteration operation <- task } func (l *ListSearch) Status() (Status, error) { if l.eval < l.rows { return NotTerminated, nil } return MethodConverge, nil } func (l *ListSearch) Run(operation chan<- Task, result <-chan Task, tasks []Task) { // Send initial tasks to evaluate for _, task := range tasks { l.sendNewLoc(operation, task) } // Read from the channel until PostIteration is sent or until the list of // tasks is exhausted. Loop: for { task := <-result switch task.Op { default: panic("unknown operation") case PostIteration: break Loop case MajorIteration: if l.eval == l.rows { task.Op = MethodDone operation <- task continue } l.sendNewLoc(operation, task) case FuncEvaluation: l.updateMajor(operation, task) } } // Post iteration was sent, or the list has been completed. Read in the final // list of tasks. for task := range result { switch task.Op { default: panic("unknown operation") case MajorIteration: case FuncEvaluation: l.updateMajor(operation, task) } } close(operation) }