From a8deabad6da7a405e284ea9c5402624567a2c47b Mon Sep 17 00:00:00 2001 From: Vladimir Chalupecky Date: Tue, 19 Jan 2016 15:28:16 +0900 Subject: [PATCH 1/8] Add ErrLinesearcherBound error --- errors.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/errors.go b/errors.go index fda377a..702430b 100644 --- a/errors.go +++ b/errors.go @@ -27,6 +27,10 @@ var ( // progress because there is no change in location after Linesearcher step // due to floating-point arithmetic. ErrNoProgress = errors.New("linesearch: no change in location after Linesearcher step") + + // ErrLinesearcherBound signifies that a Linesearcher reached a step that + // lies out of allowed bounds. + ErrLinesearcherBound = errors.New("linesearch: step out of bounds") ) // ErrFunc is returned when an initial function value is invalid. The error From 6d713f3e535a90d449f01b729346d9df011f2571 Mon Sep 17 00:00:00 2001 From: Vladimir Chalupecky Date: Mon, 9 Nov 2015 12:35:23 +0900 Subject: [PATCH 2/8] Add MoreThuente Linesearcher --- morethuente.go | 385 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 385 insertions(+) create mode 100644 morethuente.go diff --git a/morethuente.go b/morethuente.go new file mode 100644 index 0000000..ec23167 --- /dev/null +++ b/morethuente.go @@ -0,0 +1,385 @@ +// 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 "math" + +// MoreThuente is a Linesearcher that finds steps that satisfy both the +// sufficient decrease and curvature conditions (the strong Wolfe conditions). +// +// References: +// - More, J.J. and D.J. Thuente: Line Search Algorithms with Guaranteed Sufficient +// Decrease. ACM Transactions on Mathematical Software 20(3) (1994), 286-307 +type MoreThuente struct { + // DecreaseFactor is the constant factor in the sufficient decrease + // (Armijo) condition. + // It must be in the interval [0, 1). The default value is 0. + DecreaseFactor float64 + // CurvatureFactor is the constant factor in the Wolfe conditions. Smaller + // values result in a more exact line search. + // A set value must be in the interval (0, 1). If it is zero, it will be + // defaulted to 0.9. + CurvatureFactor float64 + // StepTolerance sets the minimum acceptable width for the linesearch + // interval. If the relative interval length is less than this value, + // ErrLinesearcherFailure is returned. + // It must be non-negative. If it is zero, it will be defaulted to 1e-10. + StepTolerance float64 + + // MinimumStep is the minimum step that the linesearcher will take. + // It must be non-negative and less than MaximumStep. Defaults to no + // minimum (a value of 0). + MinimumStep float64 + // MaximumStep is the maximum step that the linesearcher will take. + // It must be greater than MinimumStep. If it is zero, it will be defaulted + // to 1e20. + MaximumStep float64 + + bracketed bool // Indicates if a minimum has been bracketed. + fInit float64 // Function value at step = 0. + gInit float64 // Derivative value at step = 0. + + // When stage is 1, the algorithm updates the interval given by x and y + // so that it contains a minimizer of the modified function + // psi(step) = f(step) - f(0) - DecreaseFactor * step * f'(0). + // When stage is 2, the interval is updated so that it contains a minimizer + // of f. + stage int + + step float64 // Current step. + lower, upper float64 // Lower and upper bounds on the next step. + x float64 // Endpoint of the interval with a lower function value. + fx, gx float64 // Data at x. + y float64 // The other endpoint. + fy, gy float64 // Data at y. + width [2]float64 // Width of the interval at two previous iterations. +} + +const ( + mtMinGrowthFactor float64 = 1.1 + mtMaxGrowthFactor float64 = 4 +) + +func (mt *MoreThuente) Init(f, g float64, step float64) Operation { + // Based on the original Fortran code that is available, for example, from + // http://ftp.mcs.anl.gov/pub/MINPACK-2/csrch/ + // as part of + // MINPACK-2 Project. November 1993. + // Argonne National Laboratory and University of Minnesota. + // Brett M. Averick, Richard G. Carter, and Jorge J. Moré. + + if g >= 0 { + panic("morethuente: initial derivative is non-negative") + } + if step <= 0 { + panic("morethuente: invalid initial step") + } + + if mt.CurvatureFactor == 0 { + mt.CurvatureFactor = 0.9 + } + if mt.StepTolerance == 0 { + mt.StepTolerance = 1e-10 + } + if mt.MaximumStep == 0 { + mt.MaximumStep = 1e20 + } + + if mt.MinimumStep < 0 { + panic("morethuente: minimum step is negative") + } + if mt.MaximumStep <= mt.MinimumStep { + panic("morethuente: maximum step is not greater than minimum step") + } + if mt.DecreaseFactor < 0 || mt.DecreaseFactor >= 1 { + panic("morethuente: invalid decrease factor") + } + if mt.CurvatureFactor <= 0 || mt.CurvatureFactor >= 1 { + panic("morethuente: invalid curvature factor") + } + if mt.StepTolerance <= 0 { + panic("morethuente: step tolerance is not positive") + } + + if step < mt.MinimumStep { + step = mt.MinimumStep + } + if step > mt.MaximumStep { + step = mt.MaximumStep + } + + mt.bracketed = false + mt.stage = 1 + mt.fInit = f + mt.gInit = g + + mt.x, mt.fx, mt.gx = 0, f, g + mt.y, mt.fy, mt.gy = 0, f, g + + mt.lower = 0 + mt.upper = step + mtMaxGrowthFactor*step + + mt.width[0] = mt.MaximumStep - mt.MinimumStep + mt.width[1] = 2 * mt.width[0] + + mt.step = step + return FuncEvaluation | GradEvaluation +} + +func (mt *MoreThuente) Iterate(f, g float64) (Operation, float64, error) { + if mt.stage == 0 { + panic("morethuente: Init has not been called") + } + + gTest := mt.DecreaseFactor * mt.gInit + fTest := mt.fInit + mt.step*gTest + + if mt.bracketed { + if mt.step <= mt.lower || mt.step >= mt.upper || mt.upper-mt.lower <= mt.StepTolerance*mt.upper { + // step contains the best step found (see below). + return NoOperation, mt.step, ErrLinesearcherFailure + } + } + if mt.step == mt.MaximumStep && f <= fTest && g <= gTest { + return NoOperation, mt.step, ErrLinesearcherBound + } + if mt.step == mt.MinimumStep && (f > fTest || g >= gTest) { + return NoOperation, mt.step, ErrLinesearcherFailure + } + + // Test for convergence. + if f <= fTest && math.Abs(g) <= mt.CurvatureFactor*(-mt.gInit) { + mt.stage = 0 + return MajorIteration, mt.step, nil + } + + if mt.stage == 1 && f <= fTest && g >= 0 { + mt.stage = 2 + } + + if mt.stage == 1 && f <= mt.fx && f > fTest { + // Lower function value but the decrease is not sufficient . + + // Compute values and derivatives of the modified function at step, x, y. + fm := f - mt.step*gTest + fxm := mt.fx - mt.x*gTest + fym := mt.fy - mt.y*gTest + gm := g - gTest + gxm := mt.gx - gTest + gym := mt.gy - gTest + // Update x, y and step. + mt.nextStep(fxm, gxm, fym, gym, fm, gm) + // Recover values and derivates of the non-modified function at x and y. + mt.fx = fxm + mt.x*gTest + mt.fy = fym + mt.y*gTest + mt.gx = gxm + gTest + mt.gy = gym + gTest + } else { + // Update x, y and step. + mt.nextStep(mt.fx, mt.gx, mt.fy, mt.gy, f, g) + } + + if mt.bracketed { + // Monitor the length of the bracketing interval. If the interval has + // not been reduced sufficiently after two steps, use bisection to + // force its length to zero. + width := mt.y - mt.x + if math.Abs(width) >= 2.0/3*mt.width[1] { + mt.step = mt.x + 0.5*width + } + mt.width[0], mt.width[1] = math.Abs(width), mt.width[0] + } + + if mt.bracketed { + mt.lower = math.Min(mt.x, mt.y) + mt.upper = math.Max(mt.x, mt.y) + } else { + mt.lower = mt.step + mtMinGrowthFactor*(mt.step-mt.x) + mt.upper = mt.step + mtMaxGrowthFactor*(mt.step-mt.x) + } + + // Force the step to be in [MinimumStep, MaximumStep]. + mt.step = math.Max(mt.MinimumStep, math.Min(mt.step, mt.MaximumStep)) + + if mt.bracketed { + if mt.step <= mt.lower || mt.step >= mt.upper || mt.upper-mt.lower <= mt.StepTolerance*mt.upper { + // If further progress is not possible, set step to the best step + // obtained during the search. + mt.step = mt.x + } + } + + return FuncEvaluation | GradEvaluation, mt.step, nil +} + +// nextStep computes the next safeguarded step and updates the interval that +// contains a step that satisfies the sufficient decrease and curvature +// conditions. +func (mt *MoreThuente) nextStep(fx, gx, fy, gy, f, g float64) { + x := mt.x + y := mt.y + step := mt.step + + gNeg := g < 0 + if gx < 0 { + gNeg = !gNeg + } + + var next float64 + var bracketed bool + switch { + case f > fx: + // A higher function value. The minimum is bracketed between x and step. + // We want the next step to be closer to x because the function value + // there is lower. + + theta := 3*(fx-f)/(step-x) + gx + g + s := math.Max(math.Abs(gx), math.Abs(g)) + s = math.Max(s, math.Abs(theta)) + gamma := s * math.Sqrt((theta/s)*(theta/s)-(gx/s)*(g/s)) + if step < x { + gamma *= -1 + } + p := gamma - gx + theta + q := gamma - gx + gamma + g + r := p / q + stpc := x + r*(step-x) + stpq := x + gx/((fx-f)/(step-x)+gx)/2*(step-x) + + if math.Abs(stpc-x) < math.Abs(stpq-x) { + // The cubic step is closer to x than the quadratic step. + // Take the cubic step. + next = stpc + } else { + // If f is much larger than fx, then the quadratic step may be too + // close to x. Therefore heuristically take the average of the + // cubic and quadratic steps. + next = stpc + (stpq-stpc)/2 + } + bracketed = true + + case gNeg: + // A lower function value and derivatives of opposite sign. The minimum + // is bracketed between x and step. If we choose a step that is far + // from step, the next iteration will also likely fall in this case. + + theta := 3*(fx-f)/(step-x) + gx + g + s := math.Max(math.Abs(gx), math.Abs(g)) + s = math.Max(s, math.Abs(theta)) + gamma := s * math.Sqrt((theta/s)*(theta/s)-(gx/s)*(g/s)) + if step > x { + gamma *= -1 + } + p := gamma - g + theta + q := gamma - g + gamma + gx + r := p / q + stpc := step + r*(x-step) + stpq := step + g/(g-gx)*(x-step) + + if math.Abs(stpc-step) > math.Abs(stpq-step) { + // The cubic step is farther from x than the quadratic step. + // Take the cubic step. + next = stpc + } else { + // Take the quadratic step. + next = stpq + } + bracketed = true + + case math.Abs(g) < math.Abs(gx): + // A lower function value, derivatives of the same sign, and the + // magnitude of the derivative decreases. Extrapolate function values + // at x and step so that the next step lies between step and y. + + theta := 3*(fx-f)/(step-x) + gx + g + s := math.Max(math.Abs(gx), math.Abs(g)) + s = math.Max(s, math.Abs(theta)) + gamma := s * math.Sqrt(math.Max(0, (theta/s)*(theta/s)-(gx/s)*(g/s))) + if step > x { + gamma *= -1 + } + p := gamma - g + theta + q := gamma + gx - g + gamma + r := p / q + var stpc float64 + switch { + case r < 0 && gamma != 0: + stpc = step + r*(x-step) + case step > x: + stpc = mt.upper + default: + stpc = mt.lower + } + stpq := step + g/(g-gx)*(x-step) + + if mt.bracketed { + // We are extrapolating so be cautious and take the step that + // is closer to step. + if math.Abs(stpc-step) < math.Abs(stpq-step) { + next = stpc + } else { + next = stpq + } + // Modify next if it is close to or beyond y. + if step > x { + next = math.Min(step+2.0/3*(y-step), next) + } else { + next = math.Max(step+2.0/3*(y-step), next) + } + } else { + // Minimum has not been bracketed so take the larger step... + if math.Abs(stpc-step) > math.Abs(stpq-step) { + next = stpc + } else { + next = stpq + } + // ...but within reason. + next = math.Max(mt.lower, math.Min(next, mt.upper)) + } + + default: + // A lower function value, derivatives of the same sign, and the + // magnitude of the derivative does not decrease. The function seems to + // decrease rapidly in the direction of the step. + + switch { + case mt.bracketed: + theta := 3*(f-fy)/(y-step) + gy + g + s := math.Max(math.Abs(gy), math.Abs(g)) + s = math.Max(s, math.Abs(theta)) + gamma := s * math.Sqrt((theta/s)*(theta/s)-(gy/s)*(g/s)) + if step > y { + gamma *= -1 + } + p := gamma - g + theta + q := gamma - g + gamma + gy + r := p / q + next = step + r*(y-step) + case step > x: + next = mt.upper + default: + next = mt.lower + } + } + + if f > fx { + // x is still the best step. + mt.y = step + mt.fy = f + mt.gy = g + } else { + // step is the new best step. + if gNeg { + mt.y = x + mt.fy = fx + mt.gy = gx + } + mt.x = step + mt.fx = f + mt.gx = g + } + mt.bracketed = bracketed + mt.step = next +} From 78333bfbad929330d6ea4d6889e116e2ecb83858 Mon Sep 17 00:00:00 2001 From: Vladimir Chalupecky Date: Tue, 19 Jan 2016 09:28:23 +0900 Subject: [PATCH 3/8] functions: add ConcaveRight function --- functions/functions.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/functions/functions.go b/functions/functions.go index 38810de..2e15923 100644 --- a/functions/functions.go +++ b/functions/functions.go @@ -1627,3 +1627,29 @@ func (Wood) Minima() []Minimum { }, } } + +// ConcaveRight implements an univariate function that is concave to the right +// of the minimizer which is located at x=sqrt(2). +// +// References: +// More, J.J., and Thuente, D.J.: Line Search Algorithms with Guaranteed Sufficient Decrease. +// ACM Transactions on Mathematical Software 20(3) (1994), 286–307, eq. (5.1) +type ConcaveRight struct{} + +func (ConcaveRight) Func(x []float64) float64 { + if len(x) != 1 { + panic("dimension of the problem must be 1") + } + return -x[0] / (x[0]*x[0] + 2) +} + +func (ConcaveRight) Grad(grad, x []float64) { + if len(x) != 1 { + panic("dimension of the problem must be 1") + } + if len(x) != len(grad) { + panic("incorrect size of the gradient") + } + xSqr := x[0] * x[0] + grad[0] = (xSqr - 2) / (xSqr + 2) / (xSqr + 2) +} From cfae506f563ff681b4a35820d3b5660a4655f019 Mon Sep 17 00:00:00 2001 From: Vladimir Chalupecky Date: Tue, 19 Jan 2016 09:45:47 +0900 Subject: [PATCH 4/8] functions: add ConcaveLeft function --- functions/functions.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/functions/functions.go b/functions/functions.go index 2e15923..bd4b820 100644 --- a/functions/functions.go +++ b/functions/functions.go @@ -1653,3 +1653,28 @@ func (ConcaveRight) Grad(grad, x []float64) { xSqr := x[0] * x[0] grad[0] = (xSqr - 2) / (xSqr + 2) / (xSqr + 2) } + +// ConcaveLeft implements an univariate function that is concave to the left of +// the minimizer which is located at x=399/250=1.596. +// +// References: +// More, J.J., and Thuente, D.J.: Line Search Algorithms with Guaranteed Sufficient Decrease. +// ACM Transactions on Mathematical Software 20(3) (1994), 286–307, eq. (5.2) +type ConcaveLeft struct{} + +func (ConcaveLeft) Func(x []float64) float64 { + if len(x) != 1 { + panic("dimension of the problem must be 1") + } + return math.Pow(x[0]+0.004, 4) * (x[0] - 1.996) +} + +func (ConcaveLeft) Grad(grad, x []float64) { + if len(x) != 1 { + panic("dimension of the problem must be 1") + } + if len(x) != len(grad) { + panic("incorrect size of the gradient") + } + grad[0] = math.Pow(x[0]+0.004, 3) * (5*x[0] - 7.98) +} From 0e67fdcc8b791b597ac409da785d2e791ff8fa87 Mon Sep 17 00:00:00 2001 From: Vladimir Chalupecky Date: Mon, 18 Jan 2016 17:00:16 +0900 Subject: [PATCH 5/8] functions: add Plassmann function --- functions/functions.go | 55 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/functions/functions.go b/functions/functions.go index bd4b820..c923e4a 100644 --- a/functions/functions.go +++ b/functions/functions.go @@ -1678,3 +1678,58 @@ func (ConcaveLeft) Grad(grad, x []float64) { } grad[0] = math.Pow(x[0]+0.004, 3) * (5*x[0] - 7.98) } + +// Plassmann implements an univariate oscillatory function where the value of L +// controls the number of oscillations. The value of Beta controls the size of +// the derivative at zero and the size of the interval where the strong Wolfe +// conditions can hold. For small values of Beta this function represents a +// difficult test problem for linesearchers also because the information based +// on the derivative is unreliable due to the oscillations. +// +// References: +// More, J.J., and Thuente, D.J.: Line Search Algorithms with Guaranteed Sufficient Decrease. +// ACM Transactions on Mathematical Software 20(3) (1994), 286–307, eq. (5.3) +type Plassmann struct { + L float64 // Number of oscillations for |x-1| ≥ Beta. + Beta float64 // Size of the derivative at zero, f'(0) = -Beta. +} + +func (f Plassmann) Func(x []float64) float64 { + if len(x) != 1 { + panic("dimension of the problem must be 1") + } + a := x[0] + b := f.Beta + l := f.L + r := 2 * (1 - b) / l / math.Pi * math.Sin(l*math.Pi/2*a) + switch { + case a <= 1-b: + r += 1 - a + case 1-b < a && a <= 1+b: + r += 0.5 * ((a-1)*(a-1)/b + b) + default: // a > 1+b + r += a - 1 + } + return r +} + +func (f Plassmann) Grad(grad, x []float64) { + if len(x) != 1 { + panic("dimension of the problem must be 1") + } + if len(x) != len(grad) { + panic("incorrect size of the gradient") + } + a := x[0] + b := f.Beta + l := f.L + grad[0] = (1 - b) * math.Cos(l*math.Pi/2*a) + switch { + case a <= 1-b: + grad[0] -= 1 + case 1-b < a && a <= 1+b: + grad[0] += (a - 1) / b + default: // a > 1+b + grad[0] += 1 + } +} From 6f5e8675effbdf933228740c2ff58dd7bee0ae85 Mon Sep 17 00:00:00 2001 From: Vladimir Chalupecky Date: Mon, 18 Jan 2016 17:22:27 +0900 Subject: [PATCH 6/8] functions: add YanaiOzawaKaneko function --- functions/functions.go | 43 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/functions/functions.go b/functions/functions.go index c923e4a..c200191 100644 --- a/functions/functions.go +++ b/functions/functions.go @@ -1733,3 +1733,46 @@ func (f Plassmann) Grad(grad, x []float64) { grad[0] += 1 } } + +// YanaiOzawaKaneko is an univariate convex function where the values of Beta1 +// and Beta2 control the curvature around the minimum. Far away from the +// minimum the function approximates an absolute value function. Near the +// minimum, the function can either be sharply curved or flat, controlled by +// the parameter values. +// +// References: +// - More, J.J., and Thuente, D.J.: Line Search Algorithms with Guaranteed Sufficient Decrease. +// ACM Transactions on Mathematical Software 20(3) (1994), 286–307, eq. (5.4) +// - Yanai, H., Ozawa, M., and Kaneko, S.: Interpolation methods in one dimensional +// optimization. Computing 27 (1981), 155–163 +type YanaiOzawaKaneko struct { + Beta1 float64 + Beta2 float64 +} + +func (f YanaiOzawaKaneko) Func(x []float64) float64 { + if len(x) != 1 { + panic("dimension of the problem must be 1") + } + a := x[0] + b1 := f.Beta1 + b2 := f.Beta2 + g1 := math.Sqrt(1+b1*b1) - b1 + g2 := math.Sqrt(1+b2*b2) - b2 + return g1*math.Sqrt((a-1)*(a-1)+b2*b2) + g2*math.Sqrt(a*a+b1*b1) +} + +func (f YanaiOzawaKaneko) Grad(grad, x []float64) { + if len(x) != 1 { + panic("dimension of the problem must be 1") + } + if len(x) != len(grad) { + panic("incorrect size of the gradient") + } + a := x[0] + b1 := f.Beta1 + b2 := f.Beta2 + g1 := math.Sqrt(1+b1*b1) - b1 + g2 := math.Sqrt(1+b2*b2) - b2 + grad[0] = g1*(a-1)/math.Sqrt(b2*b2+(a-1)*(a-1)) + g2*a/math.Sqrt(b1*b1+a*a) +} From c930e4380ef1616d2248eeb0d647f51798d45879 Mon Sep 17 00:00:00 2001 From: Vladimir Chalupecky Date: Mon, 9 Nov 2015 17:33:09 +0900 Subject: [PATCH 7/8] Add tests for Linesearchers --- linesearcher_test.go | 136 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 linesearcher_test.go diff --git a/linesearcher_test.go b/linesearcher_test.go new file mode 100644 index 0000000..ef5417d --- /dev/null +++ b/linesearcher_test.go @@ -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: + 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 + } + } + } + } +} From 12b79c61a743938a61a082b43867af88a0237eec Mon Sep 17 00:00:00 2001 From: Vladimir Chalupecky Date: Tue, 10 Nov 2015 10:17:51 +0900 Subject: [PATCH 8/8] Use MoreThuente in CG as default Linesearcher --- cg.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cg.go b/cg.go index 672297e..f354220 100644 --- a/cg.go +++ b/cg.go @@ -103,7 +103,7 @@ func (cg *CG) Init(loc *Location) (Operation, error) { } if cg.Linesearcher == nil { - cg.Linesearcher = &Bisection{GradConst: 0.1} + cg.Linesearcher = &MoreThuente{CurvatureFactor: 0.1} } if cg.Variant == nil { cg.Variant = &HestenesStiefel{}