mirror of
https://github.com/gonum/gonum.git
synced 2025-09-30 04:52:17 +08:00
134 lines
3.6 KiB
Go
134 lines
3.6 KiB
Go
// Copyright ©2015 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 functions provides objective functions for testing optimization
|
|
// algorithms.
|
|
//
|
|
// We encourage outside contributions of additional test functions that exhibit
|
|
// properties not already covered in the testing suite or that have
|
|
// significance due to prior use as benchmark cases.
|
|
package functions // import "gonum.org/v1/gonum/optimize/functions"
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
|
|
"gonum.org/v1/gonum/diff/fd"
|
|
"gonum.org/v1/gonum/floats"
|
|
)
|
|
|
|
// function represents an objective function.
|
|
type function interface {
|
|
Func(x []float64) float64
|
|
}
|
|
|
|
type gradient interface {
|
|
Grad(grad, x []float64)
|
|
}
|
|
|
|
// minimumer is an objective function that can also provide information about
|
|
// its minima.
|
|
type minimumer interface {
|
|
function
|
|
|
|
// Minima returns _known_ minima of the function.
|
|
Minima() []Minimum
|
|
}
|
|
|
|
// Minimum represents information about an optimal location of a function.
|
|
type Minimum struct {
|
|
// X is the location of the minimum. X may not be nil.
|
|
X []float64
|
|
// F is the value of the objective function at X.
|
|
F float64
|
|
// Global indicates if the location is a global minimum.
|
|
Global bool
|
|
}
|
|
|
|
type funcTest struct {
|
|
X []float64
|
|
|
|
// F is the expected function value at X.
|
|
F float64
|
|
// Gradient is the expected gradient at X. If nil, it is not evaluated.
|
|
Gradient []float64
|
|
}
|
|
|
|
// TODO(vladimir-ch): Decide and implement an exported testing function:
|
|
// func Test(f Function, ??? ) ??? {
|
|
// }
|
|
|
|
const (
|
|
defaultTol = 1e-12
|
|
defaultGradTol = 1e-9
|
|
defaultFDGradTol = 1e-5
|
|
)
|
|
|
|
// testFunction checks that the function can evaluate itself (and its gradient)
|
|
// correctly.
|
|
func testFunction(f function, ftests []funcTest, t *testing.T) {
|
|
// Make a copy of tests because we may append to the slice.
|
|
tests := make([]funcTest, len(ftests))
|
|
copy(tests, ftests)
|
|
|
|
// Get information about the function.
|
|
fMinima, isMinimumer := f.(minimumer)
|
|
fGradient, isGradient := f.(gradient)
|
|
|
|
// If the function is a Minimumer, append its minima to the tests.
|
|
if isMinimumer {
|
|
for _, minimum := range fMinima.Minima() {
|
|
// Allocate gradient only if the function can evaluate it.
|
|
var grad []float64
|
|
if isGradient {
|
|
grad = make([]float64, len(minimum.X))
|
|
}
|
|
tests = append(tests, funcTest{
|
|
X: minimum.X,
|
|
F: minimum.F,
|
|
Gradient: grad,
|
|
})
|
|
}
|
|
}
|
|
|
|
for i, test := range tests {
|
|
F := f.Func(test.X)
|
|
|
|
// Check that the function value is as expected.
|
|
if math.Abs(F-test.F) > defaultTol {
|
|
t.Errorf("Test #%d: function value given by Func is incorrect. Want: %v, Got: %v",
|
|
i, test.F, F)
|
|
}
|
|
|
|
if test.Gradient == nil {
|
|
continue
|
|
}
|
|
|
|
// Evaluate the finite difference gradient.
|
|
fdGrad := fd.Gradient(nil, f.Func, test.X, &fd.Settings{
|
|
Formula: fd.Central,
|
|
Step: 1e-6,
|
|
})
|
|
|
|
// Check that the finite difference and expected gradients match.
|
|
if !floats.EqualApprox(fdGrad, test.Gradient, defaultFDGradTol) {
|
|
dist := floats.Distance(fdGrad, test.Gradient, math.Inf(1))
|
|
t.Errorf("Test #%d: numerical and expected gradients do not match. |fdGrad - WantGrad|_∞ = %v",
|
|
i, dist)
|
|
}
|
|
|
|
// If the function is a Gradient, check that it computes the gradient correctly.
|
|
if isGradient {
|
|
grad := make([]float64, len(test.Gradient))
|
|
fGradient.Grad(grad, test.X)
|
|
|
|
if !floats.EqualApprox(grad, test.Gradient, defaultGradTol) {
|
|
dist := floats.Distance(grad, test.Gradient, math.Inf(1))
|
|
t.Errorf("Test #%d: gradient given by Grad is incorrect. |grad - WantGrad|_∞ = %v",
|
|
i, dist)
|
|
}
|
|
}
|
|
}
|
|
}
|