gonum.org/v1/gonum@v0.14.0/optimize/linesearcher_test.go (about)

     1  // Copyright ©2015 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package optimize
     6  
     7  import (
     8  	"fmt"
     9  	"math"
    10  	"reflect"
    11  	"testing"
    12  
    13  	"gonum.org/v1/gonum/optimize/functions"
    14  )
    15  
    16  func TestMoreThuente(t *testing.T) {
    17  	t.Parallel()
    18  	d := 0.001
    19  	c := 0.001
    20  	ls := &MoreThuente{
    21  		DecreaseFactor:  d,
    22  		CurvatureFactor: c,
    23  	}
    24  	testLinesearcher(t, ls, d, c, true)
    25  }
    26  
    27  func TestBisection(t *testing.T) {
    28  	t.Parallel()
    29  	c := 0.1
    30  	ls := &Bisection{
    31  		CurvatureFactor: c,
    32  	}
    33  	testLinesearcher(t, ls, 0, c, true)
    34  }
    35  
    36  func TestBacktracking(t *testing.T) {
    37  	t.Parallel()
    38  	d := 0.001
    39  	ls := &Backtracking{
    40  		DecreaseFactor: d,
    41  	}
    42  	testLinesearcher(t, ls, d, 0, false)
    43  }
    44  
    45  type funcGrader interface {
    46  	Func([]float64) float64
    47  	Grad([]float64, []float64)
    48  }
    49  
    50  type linesearcherTest struct {
    51  	name string
    52  	f    func(float64) float64
    53  	g    func(float64) float64
    54  }
    55  
    56  func newLinesearcherTest(name string, fg funcGrader) linesearcherTest {
    57  	grad := make([]float64, 1)
    58  	return linesearcherTest{
    59  		name: name,
    60  		f: func(x float64) float64 {
    61  			return fg.Func([]float64{x})
    62  		},
    63  		g: func(x float64) float64 {
    64  			fg.Grad(grad, []float64{x})
    65  			return grad[0]
    66  		},
    67  	}
    68  }
    69  
    70  func testLinesearcher(t *testing.T, ls Linesearcher, decrease, curvature float64, strongWolfe bool) {
    71  	for i, prob := range []linesearcherTest{
    72  		newLinesearcherTest("Concave-to-the-right function", functions.ConcaveRight{}),
    73  		newLinesearcherTest("Concave-to-the-left function", functions.ConcaveLeft{}),
    74  		newLinesearcherTest("Plassmann wiggly function (l=39, beta=0.01)", functions.Plassmann{L: 39, Beta: 0.01}),
    75  		newLinesearcherTest("Yanai-Ozawa-Kaneko function (beta1=0.001, beta2=0.001)", functions.YanaiOzawaKaneko{Beta1: 0.001, Beta2: 0.001}),
    76  		newLinesearcherTest("Yanai-Ozawa-Kaneko function (beta1=0.01, beta2=0.001)", functions.YanaiOzawaKaneko{Beta1: 0.01, Beta2: 0.001}),
    77  		newLinesearcherTest("Yanai-Ozawa-Kaneko function (beta1=0.001, beta2=0.01)", functions.YanaiOzawaKaneko{Beta1: 0.001, Beta2: 0.01}),
    78  	} {
    79  		for _, initStep := range []float64{0.001, 0.1, 1, 10, 1000} {
    80  			prefix := fmt.Sprintf("test %d (%v started from %v)", i, prob.name, initStep)
    81  
    82  			f0 := prob.f(0)
    83  			g0 := prob.g(0)
    84  			if g0 >= 0 {
    85  				panic("bad test function")
    86  			}
    87  
    88  			op := ls.Init(f0, g0, initStep)
    89  			if !op.isEvaluation() {
    90  				t.Errorf("%v: Linesearcher.Init returned non-evaluating operation %v", prefix, op)
    91  				continue
    92  			}
    93  
    94  			var (
    95  				err  error
    96  				k    int
    97  				f, g float64
    98  				step float64
    99  			)
   100  		loop:
   101  			for {
   102  				switch op {
   103  				case MajorIteration:
   104  					if f > f0+step*decrease*g0 {
   105  						t.Errorf("%v: %v found step %v that does not satisfy the sufficient decrease condition",
   106  							prefix, reflect.TypeOf(ls), step)
   107  					}
   108  					if strongWolfe && math.Abs(g) > curvature*(-g0) {
   109  						t.Errorf("%v: %v found step %v that does not satisfy the curvature condition",
   110  							prefix, reflect.TypeOf(ls), step)
   111  					}
   112  					break loop
   113  				case FuncEvaluation:
   114  					f = prob.f(step)
   115  				case GradEvaluation:
   116  					g = prob.g(step)
   117  				case FuncEvaluation | GradEvaluation:
   118  					f = prob.f(step)
   119  					g = prob.g(step)
   120  				default:
   121  					t.Errorf("%v: Linesearcher returned an invalid operation %v", prefix, op)
   122  					break loop
   123  				}
   124  
   125  				k++
   126  				if k == 1000 {
   127  					t.Errorf("%v: %v did not finish", prefix, reflect.TypeOf(ls))
   128  					break
   129  				}
   130  
   131  				op, step, err = ls.Iterate(f, g)
   132  				if err != nil {
   133  					t.Errorf("%v: %v failed at step %v with %v", prefix, reflect.TypeOf(ls), step, err)
   134  					break
   135  				}
   136  			}
   137  		}
   138  	}
   139  }