forked from afbarnard/go-lbfgsb
-
Notifications
You must be signed in to change notification settings - Fork 3
/
optim.go
224 lines (199 loc) · 6.8 KB
/
optim.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
// Copyright (c) 2014 Aubrey Barnard. This is free software. See
// LICENSE.txt for details.
// General interface to optimization algorithms.
package lbfgsb
import (
"fmt"
)
////////////////////////////////////////
// Optimization algorithms
// ObjectiveFunctionMinimizer is the interface for all n-dimensional
// numerical minimization optimization algorithms that use gradients.
// Finds a local minimum. The idea is that an optimization algorithm
// object will provide this method as well as methods for setting
// parameters, getting performance results, logging, etc., methods that
// are specific to the implementation. This interface should specify
// the minimum necessary to be a useful optimization algorithm.
type ObjectiveFunctionMinimizer interface {
// Minimize finds a numerically-approximate minimum of the given
// objective function starting from the given point. Returns the
// minimum (or the best point found) and the status of the algorithm
// at exit.
Minimize(objective FunctionWithGradient, initialPoint []float64) (
minimum PointValueGradient, exitStatus ExitStatus)
}
////////////////////////////////////////
// Optimization inputs
// FunctionWithGradient is the interface for a function (f: R**n -> R)
// and its gradient (f': R**n -> R**n) suitable for use as an objective
// function for optimization.
type FunctionWithGradient interface {
// EvaluateFunction returns the value of the function at the given
// point.
EvaluateFunction(point []float64) float64
// EvaluateGradient returns the gradient of the function at the
// given point.
EvaluateGradient(point []float64) []float64
}
// GeneralObjectiveFunction is a utility object that combines individual
// Go functions into a FunctionWithGradient.
type GeneralObjectiveFunction struct {
Function func([]float64) float64
Gradient func([]float64) []float64
}
func (gof GeneralObjectiveFunction) EvaluateFunction(point []float64) float64 {
return gof.Function(point)
}
func (gof GeneralObjectiveFunction) EvaluateGradient(point []float64) []float64 {
return gof.Gradient(point)
}
// OptimizationIterationLogger is the type of function that
// logs/records/processes information about a single iteration in an
// optimization run.
type OptimizationIterationLogger func(info *OptimizationIterationInformation)
// OptimizationIterationInformation is a container for information about
// an optimization iteration.
type OptimizationIterationInformation struct {
Iteration int
FEvals int
GEvals int
FEvalsTotal int
GEvalsTotal int
StepLength float64
X []float64
F float64
G []float64
FDelta float64
FDeltaBound float64
GNorm float64
GNormBound float64
}
// Header returns a string with descriptions for the fields returned by
// String().
func (oii *OptimizationIterationInformation) Header() string {
return "iter, f(x), step, df(x) <1?, ||f'(x)|| <1?, #f(), #g()"
}
// String formats the iteration information fields as a row in a table.
func (oii *OptimizationIterationInformation) String() string {
// Close to convergence for F?
fConvRatio := oii.FDelta / oii.FDeltaBound
fConvIndicator := "F"
if fConvRatio < 1.0 {
fConvIndicator = "T"
}
// Close to convergence for G?
gConvRatio := oii.GNorm / oii.GNormBound
gConvIndicator := "F"
if gConvRatio < 1.0 {
gConvIndicator = "T"
}
// Put all the fields together
return fmt.Sprintf("%d %g %g %g %.2g%v %g %.2g%v %d %d",
oii.Iteration, oii.F, oii.StepLength,
oii.FDelta, fConvRatio, fConvIndicator,
oii.GNorm, gConvRatio, gConvIndicator,
oii.FEvals, oii.GEvals)
}
////////////////////////////////////////
// Optimization outputs
// PointValueGradient is a point in optimization space as well as the
// result of optimization: a point (x), its function value (f), and its
// gradient (g). The lengths of X and G must agree.
type PointValueGradient struct {
X []float64
F float64
G []float64
}
// ExitStatusCode describes the exit status of an optimization
// algorithm. Multiple statuses are necessary because success in
// optimization is not binary and so a simple error is not adequate.
// There are four exit statuses to distinguish:
//
// 1. Success. Normal termination having converged.
//
// 2. Approximate success. Normal operation resulting in a more
// approximate answer. For example, unable to meet termination
// tolerances.
//
// 3. Warning. The result could be OK, but there were some issues that
// may have reduced the quality of the result and require examination.
// For example, slight numerical problems, exceeding iteration or time
// bounds.
//
// 4. Failure of optimization. For example, a necessary condition of
// the algorithm was not met, severe numerical problems. (This status
// includes failures to evaluate the objective function or objective
// gradient.)
//
// There are also the typical runtime errors due to usage, bugs, etc.:
//
// 5. Usage error. For example, invalid constraints, bad parameters.
// Responsibility is on the caller.
//
// 6. Internal error. Other runtime or programming/logic error which
// may be a bug. Responsibility is on this package.
type ExitStatusCode uint8
// ExitStatusCode values.
const (
SUCCESS ExitStatusCode = iota
APPROXIMATE
WARNING
FAILURE
USAGE_ERROR
INTERNAL_ERROR
)
// String returns a word for each ExitStatusCode.
func (esc ExitStatusCode) String() string {
switch esc {
case SUCCESS:
return "SUCCESS"
case APPROXIMATE:
return "APPROXIMATE"
case WARNING:
return "WARNING"
case FAILURE:
return "FAILURE"
case USAGE_ERROR:
return "USAGE_ERROR"
case INTERNAL_ERROR:
return "INTERNAL_ERROR"
default:
return "UNKNOWN"
}
}
// ExitStatus is the exit status of an optimization algorithm. Includes
// a status code and a message explaining the situation.
type ExitStatus struct {
Code ExitStatusCode
Message string
}
// String returns the exit status code and message as text.
func (es ExitStatus) String() string {
return fmt.Sprintf("Exit status: %v; Message: %v;", es.Code, es.Message)
}
// Error allows this ExitStatus to be treated like an error object.
func (es ExitStatus) Error() string {
return es.String()
}
// AsError returns an error representing this exit status. If the exit
// status code is 'SUCCESS' then AsError returns nil. Otherwise returns
// an error object (which happens to be this object).
func (es ExitStatus) AsError() error {
if es.Code != SUCCESS {
return &es
}
return nil
}
// OptimizationStatistics is a container for basic statistics about an
// optimization run. Values can be negative to indicate they were not
// tracked.
type OptimizationStatistics struct {
Iterations int
FunctionEvaluations int
GradientEvaluations int
}
// OptimizationStatisticser is an object that can supply statistics
// about an optimization run.
type OptimizationStatisticser interface {
OptimizationStatistics() OptimizationStatistics
}