fixed dependencies
This commit is contained in:
602
vendor/gonum.org/v1/gonum/optimize/minimize.go
generated
vendored
Normal file
602
vendor/gonum.org/v1/gonum/optimize/minimize.go
generated
vendored
Normal file
@@ -0,0 +1,602 @@
|
||||
// Copyright ©2016 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 (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"gonum.org/v1/gonum/floats"
|
||||
"gonum.org/v1/gonum/mat"
|
||||
)
|
||||
|
||||
const (
|
||||
nonpositiveDimension string = "optimize: non-positive input dimension"
|
||||
negativeTasks string = "optimize: negative input number of tasks"
|
||||
)
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Task is a type to communicate between the Method and the outer
|
||||
// calling script.
|
||||
type Task struct {
|
||||
ID int
|
||||
Op Operation
|
||||
*Location
|
||||
}
|
||||
|
||||
// Location represents a location in the optimization procedure.
|
||||
type Location struct {
|
||||
// X is the function input for the location.
|
||||
X []float64
|
||||
// F is the result of evaluating the function at X.
|
||||
F float64
|
||||
// Gradient holds the first-order partial derivatives
|
||||
// of the function at X.
|
||||
// The length of Gradient must match the length of X
|
||||
// or be zero. If the capacity of Gradient is less
|
||||
// than the length of X, a new slice will be allocated.
|
||||
Gradient []float64
|
||||
// Hessian holds the second-order partial derivatives
|
||||
// of the function at X.
|
||||
// The dimensions of Hessian must match the length of X
|
||||
// or Hessian must be nil or empty. If Hessian is nil
|
||||
// a new mat.SymDense will be allocated, if it is empty
|
||||
// it will be resized to match the length of X.
|
||||
Hessian *mat.SymDense
|
||||
}
|
||||
|
||||
// Method is a type which can search for an optimum of an objective function.
|
||||
type Method interface {
|
||||
// Init initializes the method for optimization. The inputs are
|
||||
// the problem dimension and number of available concurrent tasks.
|
||||
//
|
||||
// Init returns the number of concurrent processes to use, which must be
|
||||
// less than or equal to tasks.
|
||||
Init(dim, tasks int) (concurrent int)
|
||||
// Run runs an optimization. The method sends Tasks on
|
||||
// the operation channel (for performing function evaluations, major
|
||||
// iterations, etc.). The result of the tasks will be returned on Result.
|
||||
// See the documentation for Operation types for the possible operations.
|
||||
//
|
||||
// The caller of Run will signal the termination of the optimization
|
||||
// (i.e. convergence from user settings) by sending a task with a PostIteration
|
||||
// Op field on result. More tasks may still be sent on operation after this
|
||||
// occurs, but only MajorIteration operations will still be conducted
|
||||
// appropriately. Thus, it can not be guaranteed that all Evaluations sent
|
||||
// on operation will be evaluated, however if an Evaluation is started,
|
||||
// the results of that evaluation will be sent on results.
|
||||
//
|
||||
// The Method must read from the result channel until it is closed.
|
||||
// During this, the Method may want to send new MajorIteration(s) on
|
||||
// operation. Method then must close operation, and return from Run.
|
||||
// These steps must establish a "happens-before" relationship between result
|
||||
// being closed (externally) and Run closing operation, for example
|
||||
// by using a range loop to read from result even if no results are expected.
|
||||
//
|
||||
// The last parameter to Run is a slice of tasks with length equal to
|
||||
// the return from Init. Task has an ID field which may be
|
||||
// set and modified by Method, and must not be modified by the caller.
|
||||
// The first element of tasks contains information about the initial location.
|
||||
// The Location.X field is always valid. The Operation field specifies which
|
||||
// other values of Location are known. If Operation == NoOperation, none of
|
||||
// the values should be used, otherwise the Evaluation operations will be
|
||||
// composed to specify the valid fields. Methods are free to use or
|
||||
// ignore these values.
|
||||
//
|
||||
// Successful execution of an Operation may require the Method to modify
|
||||
// fields a Location. MajorIteration calls will not modify the values in
|
||||
// the Location, but Evaluation operations will. Methods are encouraged to
|
||||
// leave Location fields untouched to allow memory re-use. If data needs to
|
||||
// be stored, the respective field should be set to nil -- Methods should
|
||||
// not allocate Location memory themselves.
|
||||
//
|
||||
// Method may have its own specific convergence criteria, which can
|
||||
// be communicated using a MethodDone operation. This will trigger a
|
||||
// PostIteration to be sent on result, and the MethodDone task will not be
|
||||
// returned on result. The Method must implement Statuser, and the
|
||||
// call to Status must return a Status other than NotTerminated.
|
||||
//
|
||||
// The operation and result tasks are guaranteed to have a buffer length
|
||||
// equal to the return from Init.
|
||||
Run(operation chan<- Task, result <-chan Task, tasks []Task)
|
||||
// Uses checks if the Method is suited to the optimization problem. The
|
||||
// input is the available functions in Problem to call, and the returns are
|
||||
// the functions which may be used and an error if there is a mismatch
|
||||
// between the Problem and the Method's capabilities.
|
||||
Uses(has Available) (uses Available, err error)
|
||||
}
|
||||
|
||||
// Minimize uses an optimizer to search for a minimum of a function. A
|
||||
// maximization problem can be transformed into a minimization problem by
|
||||
// multiplying the function by -1.
|
||||
//
|
||||
// The first argument represents the problem to be minimized. Its fields are
|
||||
// routines that evaluate the objective function, gradient, and other
|
||||
// quantities related to the problem. The objective function, p.Func, must not
|
||||
// be nil. The optimization method used may require other fields to be non-nil
|
||||
// as specified by method.Needs. Minimize will panic if these are not met. The
|
||||
// method can be determined automatically from the supplied problem which is
|
||||
// described below.
|
||||
//
|
||||
// If p.Status is not nil, it is called before every evaluation. If the
|
||||
// returned Status is other than NotTerminated or if the error is not nil, the
|
||||
// optimization run is terminated.
|
||||
//
|
||||
// The second argument specifies the initial location for the optimization.
|
||||
// Some Methods do not require an initial location, but initX must still be
|
||||
// specified for the dimension of the optimization problem.
|
||||
//
|
||||
// The third argument contains the settings for the minimization. If settings
|
||||
// is nil, the zero value will be used, see the documentation of the Settings
|
||||
// type for more information, and see the warning below. All settings will be
|
||||
// honored for all Methods, even if that setting is counter-productive to the
|
||||
// method. Minimize cannot guarantee strict adherence to the evaluation bounds
|
||||
// specified when performing concurrent evaluations and updates.
|
||||
//
|
||||
// The final argument is the optimization method to use. If method == nil, then
|
||||
// an appropriate default is chosen based on the properties of the other arguments
|
||||
// (dimension, gradient-free or gradient-based, etc.). If method is not nil,
|
||||
// Minimize panics if the Problem is not consistent with the Method (Uses
|
||||
// returns an error).
|
||||
//
|
||||
// Minimize returns a Result struct and any error that occurred. See the
|
||||
// documentation of Result for more information.
|
||||
//
|
||||
// See the documentation for Method for the details on implementing a method.
|
||||
//
|
||||
// Be aware that the default settings of Minimize are to accurately find the
|
||||
// minimum. For certain functions and optimization methods, this can take many
|
||||
// function evaluations. The Settings input struct can be used to limit this,
|
||||
// for example by modifying the maximum function evaluations or gradient tolerance.
|
||||
func Minimize(p Problem, initX []float64, settings *Settings, method Method) (*Result, error) {
|
||||
startTime := time.Now()
|
||||
if method == nil {
|
||||
method = getDefaultMethod(&p)
|
||||
}
|
||||
if settings == nil {
|
||||
settings = &Settings{}
|
||||
}
|
||||
stats := &Stats{}
|
||||
dim := len(initX)
|
||||
err := checkOptimization(p, dim, settings.Recorder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
optLoc := newLocation(dim) // This must have an allocated X field.
|
||||
optLoc.F = math.Inf(1)
|
||||
|
||||
initOp, initLoc := getInitLocation(dim, initX, settings.InitValues)
|
||||
|
||||
converger := settings.Converger
|
||||
if converger == nil {
|
||||
converger = defaultFunctionConverge()
|
||||
}
|
||||
converger.Init(dim)
|
||||
|
||||
stats.Runtime = time.Since(startTime)
|
||||
|
||||
// Send initial location to Recorder
|
||||
if settings.Recorder != nil {
|
||||
err = settings.Recorder.Record(optLoc, InitIteration, stats)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Run optimization
|
||||
var status Status
|
||||
status, err = minimize(&p, method, settings, converger, stats, initOp, initLoc, optLoc, startTime)
|
||||
|
||||
// Cleanup and collect results
|
||||
if settings.Recorder != nil && err == nil {
|
||||
err = settings.Recorder.Record(optLoc, PostIteration, stats)
|
||||
}
|
||||
stats.Runtime = time.Since(startTime)
|
||||
return &Result{
|
||||
Location: *optLoc,
|
||||
Stats: *stats,
|
||||
Status: status,
|
||||
}, err
|
||||
}
|
||||
|
||||
func getDefaultMethod(p *Problem) Method {
|
||||
if p.Grad != nil {
|
||||
return &LBFGS{}
|
||||
}
|
||||
return &NelderMead{}
|
||||
}
|
||||
|
||||
// minimize performs an optimization. minimize updates the settings and optLoc,
|
||||
// and returns the final Status and error.
|
||||
func minimize(prob *Problem, method Method, settings *Settings, converger Converger, stats *Stats, initOp Operation, initLoc, optLoc *Location, startTime time.Time) (Status, error) {
|
||||
dim := len(optLoc.X)
|
||||
nTasks := settings.Concurrent
|
||||
if nTasks == 0 {
|
||||
nTasks = 1
|
||||
}
|
||||
has := availFromProblem(*prob)
|
||||
_, initErr := method.Uses(has)
|
||||
if initErr != nil {
|
||||
panic(fmt.Sprintf("optimize: specified method inconsistent with Problem: %v", initErr))
|
||||
}
|
||||
newNTasks := method.Init(dim, nTasks)
|
||||
if newNTasks > nTasks {
|
||||
panic("optimize: too many tasks returned by Method")
|
||||
}
|
||||
nTasks = newNTasks
|
||||
|
||||
// Launch the method. The method communicates tasks using the operations
|
||||
// channel, and results is used to return the evaluated results.
|
||||
operations := make(chan Task, nTasks)
|
||||
results := make(chan Task, nTasks)
|
||||
go func() {
|
||||
tasks := make([]Task, nTasks)
|
||||
tasks[0].Location = initLoc
|
||||
tasks[0].Op = initOp
|
||||
for i := 1; i < len(tasks); i++ {
|
||||
tasks[i].Location = newLocation(dim)
|
||||
}
|
||||
method.Run(operations, results, tasks)
|
||||
}()
|
||||
|
||||
// Algorithmic Overview:
|
||||
// There are three pieces to performing a concurrent optimization,
|
||||
// the distributor, the workers, and the stats combiner. At a high level,
|
||||
// the distributor reads in tasks sent by method, sending evaluations to the
|
||||
// workers, and forwarding other operations to the statsCombiner. The workers
|
||||
// read these forwarded evaluation tasks, evaluate the relevant parts of Problem
|
||||
// and forward the results on to the stats combiner. The stats combiner reads
|
||||
// in results from the workers, as well as tasks from the distributor, and
|
||||
// uses them to update optimization statistics (function evaluations, etc.)
|
||||
// and to check optimization convergence.
|
||||
//
|
||||
// The complicated part is correctly shutting down the optimization. The
|
||||
// procedure is as follows. First, the stats combiner closes done and sends
|
||||
// a PostIteration to the method. The distributor then reads that done has
|
||||
// been closed, and closes the channel with the workers. At this point, no
|
||||
// more evaluation operations will be executed. As the workers finish their
|
||||
// evaluations, they forward the results onto the stats combiner, and then
|
||||
// signal their shutdown to the stats combiner. When all workers have successfully
|
||||
// finished, the stats combiner closes the results channel, signaling to the
|
||||
// method that all results have been collected. At this point, the method
|
||||
// may send MajorIteration(s) to update an optimum location based on these
|
||||
// last returned results, and then the method will close the operations channel.
|
||||
// The Method must ensure that the closing of results happens before the
|
||||
// closing of operations in order to ensure proper shutdown order.
|
||||
// Now that no more tasks will be commanded by the method, the distributor
|
||||
// closes statsChan, and with no more statistics to update the optimization
|
||||
// concludes.
|
||||
|
||||
workerChan := make(chan Task) // Delegate tasks to the workers.
|
||||
statsChan := make(chan Task) // Send evaluation updates.
|
||||
done := make(chan struct{}) // Communicate the optimization is done.
|
||||
|
||||
// Read tasks from the method and distribute as appropriate.
|
||||
distributor := func() {
|
||||
for {
|
||||
select {
|
||||
case task := <-operations:
|
||||
switch task.Op {
|
||||
case InitIteration:
|
||||
panic("optimize: Method returned InitIteration")
|
||||
case PostIteration:
|
||||
panic("optimize: Method returned PostIteration")
|
||||
case NoOperation, MajorIteration, MethodDone:
|
||||
statsChan <- task
|
||||
default:
|
||||
if !task.Op.isEvaluation() {
|
||||
panic("optimize: expecting evaluation operation")
|
||||
}
|
||||
workerChan <- task
|
||||
}
|
||||
case <-done:
|
||||
// No more evaluations will be sent, shut down the workers, and
|
||||
// read the final tasks.
|
||||
close(workerChan)
|
||||
for task := range operations {
|
||||
if task.Op == MajorIteration {
|
||||
statsChan <- task
|
||||
}
|
||||
}
|
||||
close(statsChan)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
go distributor()
|
||||
|
||||
// Evaluate the Problem concurrently.
|
||||
worker := func() {
|
||||
x := make([]float64, dim)
|
||||
for task := range workerChan {
|
||||
evaluate(prob, task.Location, task.Op, x)
|
||||
statsChan <- task
|
||||
}
|
||||
// Signal successful worker completion.
|
||||
statsChan <- Task{Op: signalDone}
|
||||
}
|
||||
for i := 0; i < nTasks; i++ {
|
||||
go worker()
|
||||
}
|
||||
|
||||
var (
|
||||
workersDone int // effective wg for the workers
|
||||
status Status
|
||||
err error
|
||||
finalStatus Status
|
||||
finalError error
|
||||
)
|
||||
|
||||
// Update optimization statistics and check convergence.
|
||||
var methodDone bool
|
||||
for task := range statsChan {
|
||||
switch task.Op {
|
||||
default:
|
||||
if !task.Op.isEvaluation() {
|
||||
panic("minimize: evaluation task expected")
|
||||
}
|
||||
updateEvaluationStats(stats, task.Op)
|
||||
status, err = checkEvaluationLimits(prob, stats, settings)
|
||||
case signalDone:
|
||||
workersDone++
|
||||
if workersDone == nTasks {
|
||||
close(results)
|
||||
}
|
||||
continue
|
||||
case NoOperation:
|
||||
// Just send the task back.
|
||||
case MajorIteration:
|
||||
status = performMajorIteration(optLoc, task.Location, stats, converger, startTime, settings)
|
||||
case MethodDone:
|
||||
methodDone = true
|
||||
status = MethodConverge
|
||||
}
|
||||
if settings.Recorder != nil && status == NotTerminated && err == nil {
|
||||
stats.Runtime = time.Since(startTime)
|
||||
// Allow err to be overloaded if the Recorder fails.
|
||||
err = settings.Recorder.Record(task.Location, task.Op, stats)
|
||||
if err != nil {
|
||||
status = Failure
|
||||
}
|
||||
}
|
||||
// If this is the first termination status, trigger the conclusion of
|
||||
// the optimization.
|
||||
if status != NotTerminated || err != nil {
|
||||
select {
|
||||
case <-done:
|
||||
default:
|
||||
finalStatus = status
|
||||
finalError = err
|
||||
results <- Task{
|
||||
Op: PostIteration,
|
||||
}
|
||||
close(done)
|
||||
}
|
||||
}
|
||||
|
||||
// Send the result back to the Problem if there are still active workers.
|
||||
if workersDone != nTasks && task.Op != MethodDone {
|
||||
results <- task
|
||||
}
|
||||
}
|
||||
// This code block is here rather than above to ensure Status() is not called
|
||||
// before Method.Run closes operations.
|
||||
if methodDone {
|
||||
statuser, ok := method.(Statuser)
|
||||
if !ok {
|
||||
panic("optimize: method returned MethodDone but is not a Statuser")
|
||||
}
|
||||
finalStatus, finalError = statuser.Status()
|
||||
if finalStatus == NotTerminated {
|
||||
panic("optimize: method returned MethodDone but a NotTerminated status")
|
||||
}
|
||||
}
|
||||
return finalStatus, finalError
|
||||
}
|
||||
|
||||
func defaultFunctionConverge() *FunctionConverge {
|
||||
return &FunctionConverge{
|
||||
Absolute: 1e-10,
|
||||
Iterations: 100,
|
||||
}
|
||||
}
|
||||
|
||||
// newLocation allocates a new location structure with an X field of the
|
||||
// appropriate size.
|
||||
func newLocation(dim int) *Location {
|
||||
return &Location{
|
||||
X: make([]float64, dim),
|
||||
}
|
||||
}
|
||||
|
||||
// getInitLocation checks the validity of initLocation and initOperation and
|
||||
// returns the initial values as a *Location.
|
||||
func getInitLocation(dim int, initX []float64, initValues *Location) (Operation, *Location) {
|
||||
loc := newLocation(dim)
|
||||
if initX == nil {
|
||||
if initValues != nil {
|
||||
panic("optimize: initValues is non-nil but no initial location specified")
|
||||
}
|
||||
return NoOperation, loc
|
||||
}
|
||||
copy(loc.X, initX)
|
||||
if initValues == nil {
|
||||
return NoOperation, loc
|
||||
} else {
|
||||
if initValues.X != nil {
|
||||
panic("optimize: location specified in InitValues (only use InitX)")
|
||||
}
|
||||
}
|
||||
loc.F = initValues.F
|
||||
op := FuncEvaluation
|
||||
if initValues.Gradient != nil {
|
||||
if len(initValues.Gradient) != dim {
|
||||
panic("optimize: initial gradient does not match problem dimension")
|
||||
}
|
||||
loc.Gradient = initValues.Gradient
|
||||
op |= GradEvaluation
|
||||
}
|
||||
if initValues.Hessian != nil {
|
||||
if initValues.Hessian.SymmetricDim() != dim {
|
||||
panic("optimize: initial Hessian does not match problem dimension")
|
||||
}
|
||||
loc.Hessian = initValues.Hessian
|
||||
op |= HessEvaluation
|
||||
}
|
||||
return op, loc
|
||||
}
|
||||
|
||||
func checkOptimization(p Problem, dim int, recorder Recorder) error {
|
||||
if p.Func == nil {
|
||||
panic(badProblem)
|
||||
}
|
||||
if dim <= 0 {
|
||||
panic("optimize: impossible problem dimension")
|
||||
}
|
||||
if p.Status != nil {
|
||||
_, err := p.Status()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if recorder != nil {
|
||||
err := recorder.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// evaluate evaluates the routines specified by the Operation at loc.X, and stores
|
||||
// the answer into loc. loc.X is copied into x before evaluating in order to
|
||||
// prevent the routines from modifying it.
|
||||
func evaluate(p *Problem, loc *Location, op Operation, x []float64) {
|
||||
if !op.isEvaluation() {
|
||||
panic(fmt.Sprintf("optimize: invalid evaluation %v", op))
|
||||
}
|
||||
copy(x, loc.X)
|
||||
if op&FuncEvaluation != 0 {
|
||||
loc.F = p.Func(x)
|
||||
}
|
||||
if op&GradEvaluation != 0 {
|
||||
// Make sure we have a destination in which to place the gradient.
|
||||
if len(loc.Gradient) == 0 {
|
||||
if cap(loc.Gradient) < len(x) {
|
||||
loc.Gradient = make([]float64, len(x))
|
||||
} else {
|
||||
loc.Gradient = loc.Gradient[:len(x)]
|
||||
}
|
||||
}
|
||||
p.Grad(loc.Gradient, x)
|
||||
}
|
||||
if op&HessEvaluation != 0 {
|
||||
// Make sure we have a destination in which to place the Hessian.
|
||||
switch {
|
||||
case loc.Hessian == nil:
|
||||
loc.Hessian = mat.NewSymDense(len(x), nil)
|
||||
case loc.Hessian.IsEmpty():
|
||||
loc.Hessian.ReuseAsSym(len(x))
|
||||
}
|
||||
p.Hess(loc.Hessian, x)
|
||||
}
|
||||
}
|
||||
|
||||
// updateEvaluationStats updates the statistics based on the operation.
|
||||
func updateEvaluationStats(stats *Stats, op Operation) {
|
||||
if op&FuncEvaluation != 0 {
|
||||
stats.FuncEvaluations++
|
||||
}
|
||||
if op&GradEvaluation != 0 {
|
||||
stats.GradEvaluations++
|
||||
}
|
||||
if op&HessEvaluation != 0 {
|
||||
stats.HessEvaluations++
|
||||
}
|
||||
}
|
||||
|
||||
// checkLocationConvergence checks if the current optimal location satisfies
|
||||
// any of the convergence criteria based on the function location.
|
||||
//
|
||||
// checkLocationConvergence returns NotTerminated if the Location does not satisfy
|
||||
// the convergence criteria given by settings. Otherwise a corresponding status is
|
||||
// returned.
|
||||
// Unlike checkLimits, checkConvergence is called only at MajorIterations.
|
||||
func checkLocationConvergence(loc *Location, settings *Settings, converger Converger) Status {
|
||||
if math.IsInf(loc.F, -1) {
|
||||
return FunctionNegativeInfinity
|
||||
}
|
||||
if loc.Gradient != nil && settings.GradientThreshold > 0 {
|
||||
norm := floats.Norm(loc.Gradient, math.Inf(1))
|
||||
if norm < settings.GradientThreshold {
|
||||
return GradientThreshold
|
||||
}
|
||||
}
|
||||
return converger.Converged(loc)
|
||||
}
|
||||
|
||||
// checkEvaluationLimits checks the optimization limits after an evaluation
|
||||
// Operation. It checks the number of evaluations (of various kinds) and checks
|
||||
// the status of the Problem, if applicable.
|
||||
func checkEvaluationLimits(p *Problem, stats *Stats, settings *Settings) (Status, error) {
|
||||
if p.Status != nil {
|
||||
status, err := p.Status()
|
||||
if err != nil || status != NotTerminated {
|
||||
return status, err
|
||||
}
|
||||
}
|
||||
if settings.FuncEvaluations > 0 && stats.FuncEvaluations >= settings.FuncEvaluations {
|
||||
return FunctionEvaluationLimit, nil
|
||||
}
|
||||
if settings.GradEvaluations > 0 && stats.GradEvaluations >= settings.GradEvaluations {
|
||||
return GradientEvaluationLimit, nil
|
||||
}
|
||||
if settings.HessEvaluations > 0 && stats.HessEvaluations >= settings.HessEvaluations {
|
||||
return HessianEvaluationLimit, nil
|
||||
}
|
||||
return NotTerminated, nil
|
||||
}
|
||||
|
||||
// checkIterationLimits checks the limits on iterations affected by MajorIteration.
|
||||
func checkIterationLimits(loc *Location, stats *Stats, settings *Settings) Status {
|
||||
if settings.MajorIterations > 0 && stats.MajorIterations >= settings.MajorIterations {
|
||||
return IterationLimit
|
||||
}
|
||||
if settings.Runtime > 0 && stats.Runtime >= settings.Runtime {
|
||||
return RuntimeLimit
|
||||
}
|
||||
return NotTerminated
|
||||
}
|
||||
|
||||
// performMajorIteration does all of the steps needed to perform a MajorIteration.
|
||||
// It increments the iteration count, updates the optimal location, and checks
|
||||
// the necessary convergence criteria.
|
||||
func performMajorIteration(optLoc, loc *Location, stats *Stats, converger Converger, startTime time.Time, settings *Settings) Status {
|
||||
optLoc.F = loc.F
|
||||
copy(optLoc.X, loc.X)
|
||||
if loc.Gradient == nil {
|
||||
optLoc.Gradient = nil
|
||||
} else {
|
||||
if optLoc.Gradient == nil {
|
||||
optLoc.Gradient = make([]float64, len(loc.Gradient))
|
||||
}
|
||||
copy(optLoc.Gradient, loc.Gradient)
|
||||
}
|
||||
stats.MajorIterations++
|
||||
stats.Runtime = time.Since(startTime)
|
||||
status := checkLocationConvergence(optLoc, settings, converger)
|
||||
if status != NotTerminated {
|
||||
return status
|
||||
}
|
||||
return checkIterationLimits(optLoc, stats, settings)
|
||||
}
|
||||
Reference in New Issue
Block a user