Skip to content
This repository was archived by the owner on Nov 23, 2018. It is now read-only.

Commit 4358607

Browse files
committed
Merge branch 'morethuente'
2 parents fef9b1a + 12b79c6 commit 4358607

File tree

5 files changed

+675
-1
lines changed

5 files changed

+675
-1
lines changed

cg.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ func (cg *CG) Init(loc *Location) (Operation, error) {
103103
}
104104

105105
if cg.Linesearcher == nil {
106-
cg.Linesearcher = &Bisection{GradConst: 0.1}
106+
cg.Linesearcher = &MoreThuente{CurvatureFactor: 0.1}
107107
}
108108
if cg.Variant == nil {
109109
cg.Variant = &HestenesStiefel{}

errors.go

+4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ var (
2727
// progress because there is no change in location after Linesearcher step
2828
// due to floating-point arithmetic.
2929
ErrNoProgress = errors.New("linesearch: no change in location after Linesearcher step")
30+
31+
// ErrLinesearcherBound signifies that a Linesearcher reached a step that
32+
// lies out of allowed bounds.
33+
ErrLinesearcherBound = errors.New("linesearch: step out of bounds")
3034
)
3135

3236
// ErrFunc is returned when an initial function value is invalid. The error

functions/functions.go

+149
Original file line numberDiff line numberDiff line change
@@ -1627,3 +1627,152 @@ func (Wood) Minima() []Minimum {
16271627
},
16281628
}
16291629
}
1630+
1631+
// ConcaveRight implements an univariate function that is concave to the right
1632+
// of the minimizer which is located at x=sqrt(2).
1633+
//
1634+
// References:
1635+
// More, J.J., and Thuente, D.J.: Line Search Algorithms with Guaranteed Sufficient Decrease.
1636+
// ACM Transactions on Mathematical Software 20(3) (1994), 286–307, eq. (5.1)
1637+
type ConcaveRight struct{}
1638+
1639+
func (ConcaveRight) Func(x []float64) float64 {
1640+
if len(x) != 1 {
1641+
panic("dimension of the problem must be 1")
1642+
}
1643+
return -x[0] / (x[0]*x[0] + 2)
1644+
}
1645+
1646+
func (ConcaveRight) Grad(grad, x []float64) {
1647+
if len(x) != 1 {
1648+
panic("dimension of the problem must be 1")
1649+
}
1650+
if len(x) != len(grad) {
1651+
panic("incorrect size of the gradient")
1652+
}
1653+
xSqr := x[0] * x[0]
1654+
grad[0] = (xSqr - 2) / (xSqr + 2) / (xSqr + 2)
1655+
}
1656+
1657+
// ConcaveLeft implements an univariate function that is concave to the left of
1658+
// the minimizer which is located at x=399/250=1.596.
1659+
//
1660+
// References:
1661+
// More, J.J., and Thuente, D.J.: Line Search Algorithms with Guaranteed Sufficient Decrease.
1662+
// ACM Transactions on Mathematical Software 20(3) (1994), 286–307, eq. (5.2)
1663+
type ConcaveLeft struct{}
1664+
1665+
func (ConcaveLeft) Func(x []float64) float64 {
1666+
if len(x) != 1 {
1667+
panic("dimension of the problem must be 1")
1668+
}
1669+
return math.Pow(x[0]+0.004, 4) * (x[0] - 1.996)
1670+
}
1671+
1672+
func (ConcaveLeft) Grad(grad, x []float64) {
1673+
if len(x) != 1 {
1674+
panic("dimension of the problem must be 1")
1675+
}
1676+
if len(x) != len(grad) {
1677+
panic("incorrect size of the gradient")
1678+
}
1679+
grad[0] = math.Pow(x[0]+0.004, 3) * (5*x[0] - 7.98)
1680+
}
1681+
1682+
// Plassmann implements an univariate oscillatory function where the value of L
1683+
// controls the number of oscillations. The value of Beta controls the size of
1684+
// the derivative at zero and the size of the interval where the strong Wolfe
1685+
// conditions can hold. For small values of Beta this function represents a
1686+
// difficult test problem for linesearchers also because the information based
1687+
// on the derivative is unreliable due to the oscillations.
1688+
//
1689+
// References:
1690+
// More, J.J., and Thuente, D.J.: Line Search Algorithms with Guaranteed Sufficient Decrease.
1691+
// ACM Transactions on Mathematical Software 20(3) (1994), 286–307, eq. (5.3)
1692+
type Plassmann struct {
1693+
L float64 // Number of oscillations for |x-1| ≥ Beta.
1694+
Beta float64 // Size of the derivative at zero, f'(0) = -Beta.
1695+
}
1696+
1697+
func (f Plassmann) Func(x []float64) float64 {
1698+
if len(x) != 1 {
1699+
panic("dimension of the problem must be 1")
1700+
}
1701+
a := x[0]
1702+
b := f.Beta
1703+
l := f.L
1704+
r := 2 * (1 - b) / l / math.Pi * math.Sin(l*math.Pi/2*a)
1705+
switch {
1706+
case a <= 1-b:
1707+
r += 1 - a
1708+
case 1-b < a && a <= 1+b:
1709+
r += 0.5 * ((a-1)*(a-1)/b + b)
1710+
default: // a > 1+b
1711+
r += a - 1
1712+
}
1713+
return r
1714+
}
1715+
1716+
func (f Plassmann) Grad(grad, x []float64) {
1717+
if len(x) != 1 {
1718+
panic("dimension of the problem must be 1")
1719+
}
1720+
if len(x) != len(grad) {
1721+
panic("incorrect size of the gradient")
1722+
}
1723+
a := x[0]
1724+
b := f.Beta
1725+
l := f.L
1726+
grad[0] = (1 - b) * math.Cos(l*math.Pi/2*a)
1727+
switch {
1728+
case a <= 1-b:
1729+
grad[0] -= 1
1730+
case 1-b < a && a <= 1+b:
1731+
grad[0] += (a - 1) / b
1732+
default: // a > 1+b
1733+
grad[0] += 1
1734+
}
1735+
}
1736+
1737+
// YanaiOzawaKaneko is an univariate convex function where the values of Beta1
1738+
// and Beta2 control the curvature around the minimum. Far away from the
1739+
// minimum the function approximates an absolute value function. Near the
1740+
// minimum, the function can either be sharply curved or flat, controlled by
1741+
// the parameter values.
1742+
//
1743+
// References:
1744+
// - More, J.J., and Thuente, D.J.: Line Search Algorithms with Guaranteed Sufficient Decrease.
1745+
// ACM Transactions on Mathematical Software 20(3) (1994), 286–307, eq. (5.4)
1746+
// - Yanai, H., Ozawa, M., and Kaneko, S.: Interpolation methods in one dimensional
1747+
// optimization. Computing 27 (1981), 155–163
1748+
type YanaiOzawaKaneko struct {
1749+
Beta1 float64
1750+
Beta2 float64
1751+
}
1752+
1753+
func (f YanaiOzawaKaneko) Func(x []float64) float64 {
1754+
if len(x) != 1 {
1755+
panic("dimension of the problem must be 1")
1756+
}
1757+
a := x[0]
1758+
b1 := f.Beta1
1759+
b2 := f.Beta2
1760+
g1 := math.Sqrt(1+b1*b1) - b1
1761+
g2 := math.Sqrt(1+b2*b2) - b2
1762+
return g1*math.Sqrt((a-1)*(a-1)+b2*b2) + g2*math.Sqrt(a*a+b1*b1)
1763+
}
1764+
1765+
func (f YanaiOzawaKaneko) Grad(grad, x []float64) {
1766+
if len(x) != 1 {
1767+
panic("dimension of the problem must be 1")
1768+
}
1769+
if len(x) != len(grad) {
1770+
panic("incorrect size of the gradient")
1771+
}
1772+
a := x[0]
1773+
b1 := f.Beta1
1774+
b2 := f.Beta2
1775+
g1 := math.Sqrt(1+b1*b1) - b1
1776+
g2 := math.Sqrt(1+b2*b2) - b2
1777+
grad[0] = g1*(a-1)/math.Sqrt(b2*b2+(a-1)*(a-1)) + g2*a/math.Sqrt(b1*b1+a*a)
1778+
}

linesearcher_test.go

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

0 commit comments

Comments
 (0)