-
Notifications
You must be signed in to change notification settings - Fork 9
Add MoreThuente Linesearcher #148
Changes from all commits
a8deaba
6d713f3
78333bf
cfae506
0e67fdc
6f5e867
c930e43
12b79c6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
// 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 optimize | ||
|
||
import ( | ||
"fmt" | ||
"math" | ||
"reflect" | ||
"testing" | ||
|
||
"github.com/gonum/optimize/functions" | ||
) | ||
|
||
func TestMoreThuente(t *testing.T) { | ||
d := 0.001 | ||
c := 0.001 | ||
ls := &MoreThuente{ | ||
DecreaseFactor: d, | ||
CurvatureFactor: c, | ||
} | ||
testLinesearcher(t, ls, d, c, true) | ||
} | ||
|
||
func TestBisection(t *testing.T) { | ||
c := 0.1 | ||
ls := &Bisection{ | ||
GradConst: c, | ||
} | ||
testLinesearcher(t, ls, 0, c, true) | ||
} | ||
|
||
func TestBacktracking(t *testing.T) { | ||
d := 0.001 | ||
ls := &Backtracking{ | ||
FuncConst: d, | ||
} | ||
testLinesearcher(t, ls, d, 0, false) | ||
} | ||
|
||
type funcGrader interface { | ||
Func([]float64) float64 | ||
Grad([]float64, []float64) | ||
} | ||
|
||
type linesearcherTest struct { | ||
name string | ||
f func(float64) float64 | ||
g func(float64) float64 | ||
} | ||
|
||
func newLinesearcherTest(name string, fg funcGrader) linesearcherTest { | ||
grad := make([]float64, 1) | ||
return linesearcherTest{ | ||
name: name, | ||
f: func(x float64) float64 { | ||
return fg.Func([]float64{x}) | ||
}, | ||
g: func(x float64) float64 { | ||
fg.Grad(grad, []float64{x}) | ||
return grad[0] | ||
}, | ||
} | ||
} | ||
|
||
func testLinesearcher(t *testing.T, ls Linesearcher, decrease, curvature float64, strongWolfe bool) { | ||
for i, prob := range []linesearcherTest{ | ||
newLinesearcherTest("Concave-to-the-right function", functions.ConcaveRight{}), | ||
newLinesearcherTest("Concave-to-the-left function", functions.ConcaveLeft{}), | ||
newLinesearcherTest("Plassmann wiggly function (l=39, beta=0.01)", functions.Plassmann{39, 0.01}), | ||
newLinesearcherTest("Yanai-Ozawa-Kaneko function (beta1=0.001, beta2=0.001)", functions.YanaiOzawaKaneko{0.001, 0.001}), | ||
newLinesearcherTest("Yanai-Ozawa-Kaneko function (beta1=0.01, beta2=0.001)", functions.YanaiOzawaKaneko{0.01, 0.001}), | ||
newLinesearcherTest("Yanai-Ozawa-Kaneko function (beta1=0.001, beta2=0.01)", functions.YanaiOzawaKaneko{0.001, 0.01}), | ||
} { | ||
for _, initStep := range []float64{0.001, 0.1, 1, 10, 1000} { | ||
prefix := fmt.Sprintf("test %d (%v started from %v)", i, prob.name, initStep) | ||
|
||
f0 := prob.f(0) | ||
g0 := prob.g(0) | ||
if g0 >= 0 { | ||
panic("bad test function") | ||
} | ||
|
||
op := ls.Init(f0, g0, initStep) | ||
if !op.isEvaluation() { | ||
t.Errorf("%v: Linesearcher.Init returned non-evaluating operation %v", op) | ||
continue | ||
} | ||
|
||
var ( | ||
err error | ||
k int | ||
f, g float64 | ||
step float64 | ||
) | ||
loop: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a nice test (and not just because of how easy it is under the new invariants!). We may want to think about generalizing it to all the line searchers, i.e. an "acceptableStep" switch or something. This would help us make the the behavior similar across the linesearchers, especially in regard to floating point noise. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is good for now though until we know how that design should be. Maybe just a "TODO" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand these two comments. Could you please elaborate what you mean by "this test" and "generalize it to all linesearchers"? I already use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, I misread the test. |
||
for { | ||
switch op { | ||
case MajorIteration: | ||
if f > f0+step*decrease*g0 { | ||
t.Errorf("%v: %v found step %v that does not satisfy the sufficient decrease condition", | ||
prefix, reflect.TypeOf(ls), step) | ||
} | ||
if strongWolfe && math.Abs(g) > curvature*(-g0) { | ||
t.Errorf("%v: %v found step %v that does not satisfy the curvature condition", | ||
prefix, reflect.TypeOf(ls), step) | ||
} | ||
break loop | ||
case FuncEvaluation: | ||
f = prob.f(step) | ||
case GradEvaluation: | ||
g = prob.g(step) | ||
case FuncEvaluation | GradEvaluation: | ||
f = prob.f(step) | ||
g = prob.g(step) | ||
default: | ||
t.Errorf("%v: Linesearcher returned an invalid operation %v", op) | ||
break loop | ||
} | ||
|
||
k++ | ||
if k == 1000 { | ||
t.Errorf("%v: %v did not finish", prefix, reflect.TypeOf(ls)) | ||
break | ||
} | ||
|
||
op, step, err = ls.Iterate(f, g) | ||
if err != nil { | ||
t.Errorf("%v: %v failed at step %v with %v", prefix, reflect.TypeOf(ls), step, err) | ||
break | ||
} | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a one sentence why this is interesting?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. This function is the most interesting of the four.