diff --git a/optimize/functions/functions.go b/optimize/functions/functions.go index e4bb4d00..e8069392 100644 --- a/optimize/functions/functions.go +++ b/optimize/functions/functions.go @@ -36,13 +36,10 @@ func (Beale) Func(x []float64) float64 { return f1*f1 + f2*f2 + f3*f3 } -func (Beale) Grad(grad, x []float64) []float64 { +func (Beale) Grad(grad, x []float64) { if len(x) != 2 { panic("dimension of the problem must be 2") } - if grad == nil { - grad = make([]float64, len(x)) - } if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -57,16 +54,13 @@ func (Beale) Grad(grad, x []float64) []float64 { grad[0] = -2 * (f1*t1 + f2*t2 + f3*t3) grad[1] = 2 * x[0] * (f1 + 2*f2*x[1] + 3*f3*x[1]*x[1]) - return grad } func (Beale) Hess(dst *mat.SymDense, x []float64) { if len(x) != 2 { panic("dimension of the problem must be 2") } - if dst.IsZero() { - *dst = *(dst.GrowSym(len(x)).(*mat.SymDense)) - } else if len(x) != dst.Symmetric() { + if len(x) != dst.Symmetric() { panic("incorrect size of the Hessian") } @@ -119,13 +113,10 @@ func (BiggsEXP2) Func(x []float64) (sum float64) { return sum } -func (BiggsEXP2) Grad(grad, x []float64) []float64 { +func (BiggsEXP2) Grad(grad, x []float64) { if len(x) != 2 { panic("dimension of the problem must be 2") } - if grad == nil { - grad = make([]float64, len(x)) - } if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -144,7 +135,6 @@ func (BiggsEXP2) Grad(grad, x []float64) []float64 { grad[0] += 2 * f * dfdx0 grad[1] += 2 * f * dfdx1 } - return grad } func (BiggsEXP2) Minima() []Minimum { @@ -181,13 +171,10 @@ func (BiggsEXP3) Func(x []float64) (sum float64) { return sum } -func (BiggsEXP3) Grad(grad, x []float64) []float64 { +func (BiggsEXP3) Grad(grad, x []float64) { if len(x) != 3 { panic("dimension of the problem must be 3") } - if grad == nil { - grad = make([]float64, len(x)) - } if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -208,7 +195,6 @@ func (BiggsEXP3) Grad(grad, x []float64) []float64 { grad[1] += 2 * f * dfdx1 grad[2] += 2 * f * dfdx2 } - return grad } func (BiggsEXP3) Minima() []Minimum { @@ -245,13 +231,10 @@ func (BiggsEXP4) Func(x []float64) (sum float64) { return sum } -func (BiggsEXP4) Grad(grad, x []float64) []float64 { +func (BiggsEXP4) Grad(grad, x []float64) { if len(x) != 4 { panic("dimension of the problem must be 4") } - if grad == nil { - grad = make([]float64, len(x)) - } if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -274,7 +257,6 @@ func (BiggsEXP4) Grad(grad, x []float64) []float64 { grad[2] += 2 * f * dfdx2 grad[3] += 2 * f * dfdx3 } - return grad } func (BiggsEXP4) Minima() []Minimum { @@ -311,13 +293,10 @@ func (BiggsEXP5) Func(x []float64) (sum float64) { return sum } -func (BiggsEXP5) Grad(grad, x []float64) []float64 { +func (BiggsEXP5) Grad(grad, x []float64) { if len(x) != 5 { panic("dimension of the problem must be 5") } - if grad == nil { - grad = make([]float64, len(x)) - } if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -342,7 +321,6 @@ func (BiggsEXP5) Grad(grad, x []float64) []float64 { grad[3] += 2 * f * dfdx3 grad[4] += 2 * f * dfdx4 } - return grad } func (BiggsEXP5) Minima() []Minimum { @@ -382,13 +360,10 @@ func (BiggsEXP6) Func(x []float64) (sum float64) { return sum } -func (BiggsEXP6) Grad(grad, x []float64) []float64 { +func (BiggsEXP6) Grad(grad, x []float64) { if len(x) != 6 { panic("dimension of the problem must be 6") } - if grad == nil { - grad = make([]float64, len(x)) - } if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -415,7 +390,6 @@ func (BiggsEXP6) Grad(grad, x []float64) []float64 { grad[4] += 2 * f * dfdx4 grad[5] += 2 * f * dfdx5 } - return grad } func (BiggsEXP6) Minima() []Minimum { @@ -466,13 +440,10 @@ func (Box3D) Func(x []float64) (sum float64) { return sum } -func (Box3D) Grad(grad, x []float64) []float64 { +func (Box3D) Grad(grad, x []float64) { if len(x) != 3 { panic("dimension of the problem must be 3") } - if grad == nil { - grad = make([]float64, len(x)) - } if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -488,7 +459,6 @@ func (Box3D) Grad(grad, x []float64) []float64 { grad[1] += -2 * f * c * math.Exp(c*x[1]) grad[2] += -2 * f * y } - return grad } func (Box3D) Minima() []Minimum { @@ -573,13 +543,10 @@ func (BrownBadlyScaled) Func(x []float64) float64 { return f1*f1 + f2*f2 + f3*f3 } -func (BrownBadlyScaled) Grad(grad, x []float64) []float64 { +func (BrownBadlyScaled) Grad(grad, x []float64) { if len(x) != 2 { panic("dimension of the problem must be 2") } - if grad == nil { - grad = make([]float64, len(x)) - } if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -589,16 +556,13 @@ func (BrownBadlyScaled) Grad(grad, x []float64) []float64 { f3 := x[0]*x[1] - 2 grad[0] = 2*f1 + 2*f3*x[1] grad[1] = 2*f2 + 2*f3*x[0] - return grad } func (BrownBadlyScaled) Hess(dst *mat.SymDense, x []float64) { if len(x) != 2 { panic("dimension of the problem must be 2") } - if dst.IsZero() { - *dst = *(dst.GrowSym(len(x)).(*mat.SymDense)) - } else if len(x) != dst.Symmetric() { + if len(x) != dst.Symmetric() { panic("incorrect size of the Hessian") } @@ -648,13 +612,10 @@ func (BrownAndDennis) Func(x []float64) (sum float64) { return sum } -func (BrownAndDennis) Grad(grad, x []float64) []float64 { +func (BrownAndDennis) Grad(grad, x []float64) { if len(x) != 4 { panic("dimension of the problem must be 4") } - if grad == nil { - grad = make([]float64, len(x)) - } if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -672,16 +633,13 @@ func (BrownAndDennis) Grad(grad, x []float64) []float64 { grad[2] += 4 * f * f2 grad[3] += 4 * f * f2 * math.Sin(c) } - return grad } func (BrownAndDennis) Hess(dst *mat.SymDense, x []float64) { if len(x) != 4 { panic("dimension of the problem must be 4") } - if dst.IsZero() { - *dst = *(dst.GrowSym(len(x)).(*mat.SymDense)) - } else if len(x) != dst.Symmetric() { + if len(x) != dst.Symmetric() { panic("incorrect size of the Hessian") } @@ -758,13 +716,10 @@ func (ExtendedPowellSingular) Func(x []float64) (sum float64) { return sum } -func (ExtendedPowellSingular) Grad(grad, x []float64) []float64 { +func (ExtendedPowellSingular) Grad(grad, x []float64) { if len(x)%4 != 0 { panic("dimension of the problem must be a multiple of 4") } - if grad == nil { - grad = make([]float64, len(x)) - } if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -781,7 +736,6 @@ func (ExtendedPowellSingular) Grad(grad, x []float64) []float64 { grad[i+2] = 10*f2 - 8*f3*t1 grad[i+3] = -10*f2 - 40*f4*t2 } - return grad } func (ExtendedPowellSingular) Minima() []Minimum { @@ -826,10 +780,7 @@ func (ExtendedRosenbrock) Func(x []float64) (sum float64) { return sum } -func (ExtendedRosenbrock) Grad(grad, x []float64) []float64 { - if grad == nil { - grad = make([]float64, len(x)) - } +func (ExtendedRosenbrock) Grad(grad, x []float64) { if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -845,7 +796,6 @@ func (ExtendedRosenbrock) Grad(grad, x []float64) []float64 { for i := 1; i < dim; i++ { grad[i] += 200 * (x[i] - x[i-1]*x[i-1]) } - return grad } func (ExtendedRosenbrock) Minima() []Minimum { @@ -952,13 +902,10 @@ func (g Gaussian) Func(x []float64) (sum float64) { return sum } -func (g Gaussian) Grad(grad, x []float64) []float64 { +func (g Gaussian) Grad(grad, x []float64) { if len(x) != 3 { panic("dimension of the problem must be 3") } - if grad == nil { - grad = make([]float64, len(x)) - } if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -976,7 +923,6 @@ func (g Gaussian) Grad(grad, x []float64) []float64 { grad[1] -= f * e * d * x[0] grad[2] += 2 * f * e * x[0] * x[1] * b } - return grad } func (Gaussian) Minima() []Minimum { @@ -1018,13 +964,10 @@ func (GulfResearchAndDevelopment) Func(x []float64) (sum float64) { return sum } -func (GulfResearchAndDevelopment) Grad(grad, x []float64) []float64 { +func (GulfResearchAndDevelopment) Grad(grad, x []float64) { if len(x) != 3 { panic("dimension of the problem must be 3") } - if grad == nil { - grad = make([]float64, len(x)) - } if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -1046,7 +989,6 @@ func (GulfResearchAndDevelopment) Grad(grad, x []float64) []float64 { grad[0] *= 2 / x[0] grad[1] *= 2 * x[2] grad[2] *= 2 - return grad } func (GulfResearchAndDevelopment) Minima() []Minimum { @@ -1100,13 +1042,10 @@ func (HelicalValley) Func(x []float64) float64 { return f1*f1 + f2*f2 + f3*f3 } -func (HelicalValley) Grad(grad, x []float64) []float64 { +func (HelicalValley) Grad(grad, x []float64) { if len(x) != 3 { panic("dimension of the problem must be 3") } - if grad == nil { - grad = make([]float64, len(x)) - } if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -1125,7 +1064,6 @@ func (HelicalValley) Grad(grad, x []float64) []float64 { grad[0] = 200 * (5*s*q*x[1] + (h-1)*r*x[0]) grad[1] = 200 * (-5*s*q*x[0] + (h-1)*r*x[1]) grad[2] = 2 * (100*s + x[2]) - return grad } func (HelicalValley) Minima() []Minimum { @@ -1183,10 +1121,7 @@ func (PenaltyI) Func(x []float64) (sum float64) { return sum } -func (PenaltyI) Grad(grad, x []float64) []float64 { - if grad == nil { - grad = make([]float64, len(x)) - } +func (PenaltyI) Grad(grad, x []float64) { if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -1198,7 +1133,6 @@ func (PenaltyI) Grad(grad, x []float64) []float64 { for i, v := range x { grad[i] = 2 * (2*s*v + 1e-5*(v-1)) } - return grad } func (PenaltyI) Minima() []Minimum { @@ -1252,10 +1186,7 @@ func (PenaltyII) Func(x []float64) (sum float64) { return sum } -func (PenaltyII) Grad(grad, x []float64) []float64 { - if grad == nil { - grad = make([]float64, len(x)) - } +func (PenaltyII) Grad(grad, x []float64) { if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -1279,7 +1210,6 @@ func (PenaltyII) Grad(grad, x []float64) []float64 { grad[i] += 1e-5 * f * math.Exp(x[i]/10) / 5 } grad[0] += 2 * (x[0] - 0.2) - return grad } func (PenaltyII) Minima() []Minimum { @@ -1325,13 +1255,10 @@ func (PowellBadlyScaled) Func(x []float64) float64 { return f1*f1 + f2*f2 } -func (PowellBadlyScaled) Grad(grad, x []float64) []float64 { +func (PowellBadlyScaled) Grad(grad, x []float64) { if len(x) != 2 { panic("dimension of the problem must be 2") } - if grad == nil { - grad = make([]float64, len(x)) - } if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -1340,16 +1267,13 @@ func (PowellBadlyScaled) Grad(grad, x []float64) []float64 { f2 := math.Exp(-x[0]) + math.Exp(-x[1]) - 1.0001 grad[0] = 2 * (1e4*f1*x[1] - f2*math.Exp(-x[0])) grad[1] = 2 * (1e4*f1*x[0] - f2*math.Exp(-x[1])) - return grad } func (PowellBadlyScaled) Hess(dst *mat.SymDense, x []float64) { if len(x) != 2 { panic("dimension of the problem must be 2") } - if dst.IsZero() { - *dst = *(dst.GrowSym(len(x)).(*mat.SymDense)) - } else if len(x) != dst.Symmetric() { + if len(x) != dst.Symmetric() { panic("incorrect size of the Hessian") } @@ -1401,10 +1325,7 @@ func (Trigonometric) Func(x []float64) (sum float64) { return sum } -func (Trigonometric) Grad(grad, x []float64) []float64 { - if grad == nil { - grad = make([]float64, len(x)) - } +func (Trigonometric) Grad(grad, x []float64) { if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -1424,7 +1345,6 @@ func (Trigonometric) Grad(grad, x []float64) []float64 { for i, v := range x { grad[i] += 2 * s2 * math.Sin(v) } - return grad } func (Trigonometric) Minima() []Minimum { @@ -1477,10 +1397,7 @@ func (VariablyDimensioned) Func(x []float64) (sum float64) { return sum } -func (VariablyDimensioned) Grad(grad, x []float64) []float64 { - if grad == nil { - grad = make([]float64, len(x)) - } +func (VariablyDimensioned) Grad(grad, x []float64) { if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -1492,7 +1409,6 @@ func (VariablyDimensioned) Grad(grad, x []float64) []float64 { for i, v := range x { grad[i] = 2 * (v - 1 + s*float64(i+1)*(1+2*s*s)) } - return grad } func (VariablyDimensioned) Minima() []Minimum { @@ -1565,10 +1481,7 @@ func (Watson) Func(x []float64) (sum float64) { return sum } -func (Watson) Grad(grad, x []float64) []float64 { - if grad == nil { - grad = make([]float64, len(x)) - } +func (Watson) Grad(grad, x []float64) { if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -1604,14 +1517,11 @@ func (Watson) Grad(grad, x []float64) []float64 { t := x[1] - x[0]*x[0] - 1 grad[0] += x[0] * (2 - 4*t) grad[1] += 2 * t - return grad } func (Watson) Hess(dst *mat.SymDense, x []float64) { dim := len(x) - if dst.IsZero() { - *dst = *(dst.GrowSym(len(x)).(*mat.SymDense)) - } else if len(x) != dst.Symmetric() { + if len(x) != dst.Symmetric() { panic("incorrect size of the Hessian") } @@ -1709,13 +1619,10 @@ func (Wood) Func(x []float64) (sum float64) { return 100*f1*f1 + f2*f2 + 90*f3*f3 + f4*f4 + 10*f5*f5 + 0.1*f6*f6 } -func (Wood) Grad(grad, x []float64) []float64 { +func (Wood) Grad(grad, x []float64) { if len(x) != 4 { panic("dimension of the problem must be 4") } - if grad == nil { - grad = make([]float64, len(x)) - } if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -1730,16 +1637,13 @@ func (Wood) Grad(grad, x []float64) []float64 { grad[1] = 2 * (100*f1 + 10*f5 + 0.1*f6) grad[2] = -2 * (180*f3*x[2] + f4) grad[3] = 2 * (90*f3 + 10*f5 - 0.1*f6) - return grad } func (Wood) Hess(dst *mat.SymDense, x []float64) { if len(x) != 4 { panic("dimension of the problem must be 4") } - if dst.IsZero() { - *dst = *(dst.GrowSym(len(x)).(*mat.SymDense)) - } else if len(x) != dst.Symmetric() { + if len(x) != dst.Symmetric() { panic("incorrect size of the Hessian") } @@ -1780,7 +1684,7 @@ func (ConcaveRight) Func(x []float64) float64 { return -x[0] / (x[0]*x[0] + 2) } -func (ConcaveRight) Grad(grad, x []float64) []float64 { +func (ConcaveRight) Grad(grad, x []float64) { if len(x) != 1 { panic("dimension of the problem must be 1") } @@ -1789,7 +1693,6 @@ func (ConcaveRight) Grad(grad, x []float64) []float64 { } xSqr := x[0] * x[0] grad[0] = (xSqr - 2) / (xSqr + 2) / (xSqr + 2) - return grad } // ConcaveLeft implements an univariate function that is concave to the left of @@ -1807,18 +1710,14 @@ func (ConcaveLeft) Func(x []float64) float64 { return math.Pow(x[0]+0.004, 4) * (x[0] - 1.996) } -func (ConcaveLeft) Grad(grad, x []float64) []float64 { +func (ConcaveLeft) Grad(grad, x []float64) { if len(x) != 1 { panic("dimension of the problem must be 1") } - if grad == nil { - grad = make([]float64, len(x)) - } 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) - return grad } // Plassmann implements an univariate oscillatory function where the value of L @@ -1855,13 +1754,10 @@ func (f Plassmann) Func(x []float64) float64 { return r } -func (f Plassmann) Grad(grad, x []float64) []float64 { +func (f Plassmann) Grad(grad, x []float64) { if len(x) != 1 { panic("dimension of the problem must be 1") } - if grad == nil { - grad = make([]float64, len(x)) - } if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -1877,7 +1773,6 @@ func (f Plassmann) Grad(grad, x []float64) []float64 { default: // a > 1+b grad[0]++ } - return grad } // YanaiOzawaKaneko is an univariate convex function where the values of Beta1 @@ -1908,13 +1803,10 @@ func (f YanaiOzawaKaneko) Func(x []float64) float64 { return g1*math.Sqrt((a-1)*(a-1)+b2*b2) + g2*math.Sqrt(a*a+b1*b1) } -func (f YanaiOzawaKaneko) Grad(grad, x []float64) []float64 { +func (f YanaiOzawaKaneko) Grad(grad, x []float64) { if len(x) != 1 { panic("dimension of the problem must be 1") } - if grad == nil { - grad = make([]float64, len(x)) - } if len(x) != len(grad) { panic("incorrect size of the gradient") } @@ -1924,5 +1816,4 @@ func (f YanaiOzawaKaneko) Grad(grad, x []float64) []float64 { 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) - return grad } diff --git a/optimize/linesearcher_test.go b/optimize/linesearcher_test.go index 919a3fa0..f6d3a839 100644 --- a/optimize/linesearcher_test.go +++ b/optimize/linesearcher_test.go @@ -41,7 +41,7 @@ func TestBacktracking(t *testing.T) { type funcGrader interface { Func([]float64) float64 - Grad([]float64, []float64) []float64 + Grad([]float64, []float64) } type linesearcherTest struct { diff --git a/optimize/minimize.go b/optimize/minimize.go index 3633919c..34e14c6a 100644 --- a/optimize/minimize.go +++ b/optimize/minimize.go @@ -477,12 +477,20 @@ func evaluate(p *Problem, loc *Location, op Operation, x []float64) { loc.F = p.Func(x) } if op&GradEvaluation != 0 { - loc.Gradient = p.Grad(loc.Gradient, x) + // Make sure we have a destination in which to place the gradient. + // TODO(kortschak): Consider making this a check of len(loc.Gradient) != 0 + // to allow reuse of the slice. + if loc.Gradient == nil { + loc.Gradient = make([]float64, len(x)) + } + p.Grad(loc.Gradient, x) } if op&HessEvaluation != 0 { // Make sure we have a destination in which to place the Hessian. + // TODO(kortschak): Consider making this a check of loc.Hessian.IsZero() + // to allow reuse of the matrix. if loc.Hessian == nil { - loc.Hessian = &mat.SymDense{} + loc.Hessian = mat.NewSymDense(len(x), nil) } p.Hess(loc.Hessian, x) } diff --git a/optimize/types.go b/optimize/types.go index 751d6740..6802d40a 100644 --- a/optimize/types.go +++ b/optimize/types.go @@ -124,13 +124,13 @@ type Problem struct { Func func(x []float64) float64 // Grad evaluates the gradient at x and returns the result. Grad may use - // (and return) the provided slice if it is non-nil, or must allocate a new - // slice otherwise. Grad must not modify x. - Grad func(grad []float64, x []float64) []float64 + // the provided slice which will be the same length as x. Grad must not + // modify x. + Grad func(grad, x []float64) // Hess evaluates the Hessian at x and stores the result in-place in hess. - // Hess must not modify x. Hess must use the provided mat.SymDense, and - // must resize it if it is zero-sized. + // Hess must not modify x. Hess may use the provided mat.SymDense which + // will have dimensions matching the length of x. Hess func(hess *mat.SymDense, x []float64) // Status reports the status of the objective function being optimized and any