// Copyright ©2014 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" "testing" "gonum.org/v1/gonum/floats" "gonum.org/v1/gonum/mat" "gonum.org/v1/gonum/optimize/functions" ) type unconstrainedTest struct { // name is the name of the test. name string // p is the optimization problem to be solved. p Problem // x is the initial guess. x []float64 // gradTol is the absolute gradient tolerance for the test. If gradTol == 0, // the default value of 1e-12 will be used. gradTol float64 // fAbsTol is the absolute function convergence for the test. If fAbsTol == 0, // the default value of 1e-12 will be used. fAbsTol float64 // fIter is the number of iterations for function convergence. If fIter == 0, // the default value of 20 will be used. fIter int // long indicates that the test takes long time to finish and will be // excluded if testing.Short returns true. long bool } func (t unconstrainedTest) String() string { dim := len(t.x) if dim <= 10 { // Print the initial X only for small-dimensional problems. return fmt.Sprintf("F: %v\nDim: %v\nInitial X: %v\nGradientThreshold: %v", t.name, dim, t.x, t.gradTol) } return fmt.Sprintf("F: %v\nDim: %v\nGradientThreshold: %v", t.name, dim, t.gradTol) } var gradFreeTests = []unconstrainedTest{ { name: "Beale", p: Problem{ Func: functions.Beale{}.Func, }, x: []float64{1, 1}, }, { name: "BiggsEXP6", p: Problem{ Func: functions.BiggsEXP6{}.Func, }, x: []float64{1, 2, 1, 1, 1, 1}, }, { name: "BrownAndDennis", p: Problem{ Func: functions.BrownAndDennis{}.Func, }, x: []float64{25, 5, -5, -1}, }, { name: "ExtendedRosenbrock", p: Problem{ Func: functions.ExtendedRosenbrock{}.Func, }, x: []float64{-10, 10}, }, { name: "ExtendedRosenbrock", p: Problem{ Func: functions.ExtendedRosenbrock{}.Func, }, x: []float64{-5, 4, 16, 3}, }, } var gradientDescentTests = []unconstrainedTest{ { name: "Beale", p: Problem{ Func: functions.Beale{}.Func, Grad: functions.Beale{}.Grad, }, x: []float64{1, 1}, }, { name: "Beale", p: Problem{ Func: functions.Beale{}.Func, Grad: functions.Beale{}.Grad, }, x: []float64{3.00001, 0.50001}, }, { name: "BiggsEXP2", p: Problem{ Func: functions.BiggsEXP2{}.Func, Grad: functions.BiggsEXP2{}.Grad, }, x: []float64{1, 2}, }, { name: "BiggsEXP2", p: Problem{ Func: functions.BiggsEXP2{}.Func, Grad: functions.BiggsEXP2{}.Grad, }, x: []float64{1.00001, 10.00001}, }, { name: "BiggsEXP3", p: Problem{ Func: functions.BiggsEXP3{}.Func, Grad: functions.BiggsEXP3{}.Grad, }, x: []float64{1, 2, 1}, }, { name: "BiggsEXP3", p: Problem{ Func: functions.BiggsEXP3{}.Func, Grad: functions.BiggsEXP3{}.Grad, }, x: []float64{1.00001, 10.00001, 3.00001}, }, { name: "ExtendedRosenbrock", p: Problem{ Func: functions.ExtendedRosenbrock{}.Func, Grad: functions.ExtendedRosenbrock{}.Grad, }, x: []float64{-1.2, 1}, gradTol: 1e-10, }, { name: "ExtendedRosenbrock", p: Problem{ Func: functions.ExtendedRosenbrock{}.Func, Grad: functions.ExtendedRosenbrock{}.Grad, }, x: []float64{1.00001, 1.00001}, gradTol: 1e-10, }, { name: "ExtendedRosenbrock", p: Problem{ Func: functions.ExtendedRosenbrock{}.Func, Grad: functions.ExtendedRosenbrock{}.Grad, }, x: []float64{-1.2, 1, -1.2}, gradTol: 1e-10, }, { name: "ExtendedRosenbrock", p: Problem{ Func: functions.ExtendedRosenbrock{}.Func, Grad: functions.ExtendedRosenbrock{}.Grad, }, x: []float64{-120, 100, 50}, long: true, }, { name: "ExtendedRosenbrock", p: Problem{ Func: functions.ExtendedRosenbrock{}.Func, Grad: functions.ExtendedRosenbrock{}.Grad, }, x: []float64{1, 1, 1}, }, { name: "ExtendedRosenbrock", p: Problem{ Func: functions.ExtendedRosenbrock{}.Func, Grad: functions.ExtendedRosenbrock{}.Grad, }, x: []float64{1.00001, 1.00001, 1.00001}, gradTol: 1e-8, }, { name: "Gaussian", p: Problem{ Func: functions.Gaussian{}.Func, Grad: functions.Gaussian{}.Grad, }, x: []float64{0.4, 1, 0}, gradTol: 1e-9, }, { name: "Gaussian", p: Problem{ Func: functions.Gaussian{}.Func, Grad: functions.Gaussian{}.Grad, }, x: []float64{0.3989561, 1.0000191, 0}, gradTol: 1e-9, }, { name: "HelicalValley", p: Problem{ Func: functions.HelicalValley{}.Func, Grad: functions.HelicalValley{}.Grad, }, x: []float64{-1, 0, 0}, }, { name: "HelicalValley", p: Problem{ Func: functions.HelicalValley{}.Func, Grad: functions.HelicalValley{}.Grad, }, x: []float64{1.00001, 0.00001, 0.00001}, }, { name: "Trigonometric", p: Problem{ Func: functions.Trigonometric{}.Func, Grad: functions.Trigonometric{}.Grad, }, x: []float64{0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1}, gradTol: 1e-7, }, { name: "Trigonometric", p: Problem{ Func: functions.Trigonometric{}.Func, Grad: functions.Trigonometric{}.Grad, }, x: []float64{0.042964, 0.043976, 0.045093, 0.046338, 0.047744, 0.049354, 0.051237, 0.195209, 0.164977, 0.060148}, gradTol: 1e-8, }, newVariablyDimensioned(2, 0), { name: "VariablyDimensioned", p: Problem{ Func: functions.VariablyDimensioned{}.Func, Grad: functions.VariablyDimensioned{}.Grad, }, x: []float64{1.00001, 1.00001}, }, newVariablyDimensioned(10, 0), { name: "VariablyDimensioned", p: Problem{ Func: functions.VariablyDimensioned{}.Func, Grad: functions.VariablyDimensioned{}.Grad, }, x: []float64{1.00001, 1.00001, 1.00001, 1.00001, 1.00001, 1.00001, 1.00001, 1.00001, 1.00001, 1.00001}, }, } var cgTests = []unconstrainedTest{ { name: "BiggsEXP4", p: Problem{ Func: functions.BiggsEXP4{}.Func, Grad: functions.BiggsEXP4{}.Grad, }, x: []float64{1, 2, 1, 1}, }, { name: "BiggsEXP4", p: Problem{ Func: functions.BiggsEXP4{}.Func, Grad: functions.BiggsEXP4{}.Grad, }, x: []float64{1.00001, 10.00001, 1.00001, 5.00001}, }, { name: "BiggsEXP5", p: Problem{ Func: functions.BiggsEXP5{}.Func, Grad: functions.BiggsEXP5{}.Grad, }, x: []float64{1, 2, 1, 1, 1}, gradTol: 1e-7, }, { name: "BiggsEXP5", p: Problem{ Func: functions.BiggsEXP5{}.Func, Grad: functions.BiggsEXP5{}.Grad, }, x: []float64{1.00001, 10.00001, 1.00001, 5.00001, 4.00001}, }, { name: "BiggsEXP6", p: Problem{ Func: functions.BiggsEXP6{}.Func, Grad: functions.BiggsEXP6{}.Grad, }, x: []float64{1, 2, 1, 1, 1, 1}, gradTol: 1e-7, }, { name: "BiggsEXP6", p: Problem{ Func: functions.BiggsEXP6{}.Func, Grad: functions.BiggsEXP6{}.Grad, }, x: []float64{1.00001, 10.00001, 1.00001, 5.00001, 4.00001, 3.00001}, gradTol: 1e-8, }, { name: "Box3D", p: Problem{ Func: functions.Box3D{}.Func, Grad: functions.Box3D{}.Grad, }, x: []float64{0, 10, 20}, }, { name: "Box3D", p: Problem{ Func: functions.Box3D{}.Func, Grad: functions.Box3D{}.Grad, }, x: []float64{1.00001, 10.00001, 1.00001}, }, { name: "Box3D", p: Problem{ Func: functions.Box3D{}.Func, Grad: functions.Box3D{}.Grad, }, x: []float64{100.00001, 100.00001, 0.00001}, }, { name: "ExtendedPowellSingular", p: Problem{ Func: functions.ExtendedPowellSingular{}.Func, Grad: functions.ExtendedPowellSingular{}.Grad, }, x: []float64{3, -1, 0, 3}, }, { name: "ExtendedPowellSingular", p: Problem{ Func: functions.ExtendedPowellSingular{}.Func, Grad: functions.ExtendedPowellSingular{}.Grad, }, x: []float64{0.00001, 0.00001, 0.00001, 0.00001}, }, { name: "ExtendedPowellSingular", p: Problem{ Func: functions.ExtendedPowellSingular{}.Func, Grad: functions.ExtendedPowellSingular{}.Grad, }, x: []float64{3, -1, 0, 3, 3, -1, 0, 3}, gradTol: 1e-8, }, { name: "ExtendedPowellSingular", p: Problem{ Func: functions.ExtendedPowellSingular{}.Func, Grad: functions.ExtendedPowellSingular{}.Grad, }, x: []float64{0.00001, 0.00001, 0.00001, 0.00001, 0.00001, 0.00001, 0.00001, 0.00001}, }, { name: "ExtendedRosenbrock", p: Problem{ Func: functions.ExtendedRosenbrock{}.Func, Grad: functions.ExtendedRosenbrock{}.Grad, }, x: []float64{-1.2, 1, -1.2, 1}, }, { name: "ExtendedRosenbrock", p: Problem{ Func: functions.ExtendedRosenbrock{}.Func, Grad: functions.ExtendedRosenbrock{}.Grad, }, x: []float64{1e4, 1e4}, gradTol: 1e-10, }, { name: "ExtendedRosenbrock", p: Problem{ Func: functions.ExtendedRosenbrock{}.Func, Grad: functions.ExtendedRosenbrock{}.Grad, }, x: []float64{1.00001, 1.00001, 1.00001, 1.00001}, gradTol: 1e-10, }, { name: "PenaltyI", p: Problem{ Func: functions.PenaltyI{}.Func, Grad: functions.PenaltyI{}.Grad, }, x: []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, gradTol: 1e-9, }, { name: "PenaltyI", p: Problem{ Func: functions.PenaltyI{}.Func, Grad: functions.PenaltyI{}.Grad, }, x: []float64{0.250007, 0.250007, 0.250007, 0.250007}, gradTol: 1e-10, }, { name: "PenaltyI", p: Problem{ Func: functions.PenaltyI{}.Func, Grad: functions.PenaltyI{}.Grad, }, x: []float64{0.1581, 0.1581, 0.1581, 0.1581, 0.1581, 0.1581, 0.1581, 0.1581, 0.1581, 0.1581}, gradTol: 1e-10, }, { name: "PenaltyII", p: Problem{ Func: functions.PenaltyII{}.Func, Grad: functions.PenaltyII{}.Grad, }, x: []float64{0.5, 0.5, 0.5, 0.5}, gradTol: 1e-8, }, { name: "PenaltyII", p: Problem{ Func: functions.PenaltyII{}.Func, Grad: functions.PenaltyII{}.Grad, }, x: []float64{0.19999, 0.19131, 0.4801, 0.51884}, gradTol: 1e-8, }, { name: "PenaltyII", p: Problem{ Func: functions.PenaltyII{}.Func, Grad: functions.PenaltyII{}.Grad, }, x: []float64{0.19998, 0.01035, 0.01960, 0.03208, 0.04993, 0.07651, 0.11862, 0.19214, 0.34732, 0.36916}, gradTol: 1e-6, }, { name: "PowellBadlyScaled", p: Problem{ Func: functions.PowellBadlyScaled{}.Func, Grad: functions.PowellBadlyScaled{}.Grad, }, x: []float64{1.09815e-05, 9.10614}, gradTol: 1e-8, }, newVariablyDimensioned(100, 1e-10), newVariablyDimensioned(1000, 1e-7), newVariablyDimensioned(10000, 1e-4), { name: "Watson", p: Problem{ Func: functions.Watson{}.Func, Grad: functions.Watson{}.Grad, }, x: []float64{0, 0, 0, 0, 0, 0}, gradTol: 1e-6, }, { name: "Watson", p: Problem{ Func: functions.Watson{}.Func, Grad: functions.Watson{}.Grad, }, x: []float64{-0.01572, 1.01243, -0.23299, 1.26043, -1.51372, 0.99299}, gradTol: 1e-6, }, { name: "Watson", p: Problem{ Func: functions.Watson{}.Func, Grad: functions.Watson{}.Grad, }, x: []float64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, gradTol: 1e-6, long: true, }, { name: "Watson", p: Problem{ Func: functions.Watson{}.Func, Grad: functions.Watson{}.Grad, }, x: []float64{-1.53070e-05, 0.99978, 0.01476, 0.14634, 1.00082, -2.61773, 4.10440, -3.14361, 1.05262}, gradTol: 1e-6, }, { name: "Wood", p: Problem{ Func: functions.Wood{}.Func, Grad: functions.Wood{}.Grad, }, x: []float64{-3, -1, -3, -1}, gradTol: 1e-6, }, } var quasiNewtonTests = []unconstrainedTest{ { name: "BiggsEXP4", p: Problem{ Func: functions.BiggsEXP4{}.Func, Grad: functions.BiggsEXP4{}.Grad, }, x: []float64{1, 2, 1, 1}, }, { name: "BiggsEXP4", p: Problem{ Func: functions.BiggsEXP4{}.Func, Grad: functions.BiggsEXP4{}.Grad, }, x: []float64{1.00001, 10.00001, 1.00001, 5.00001}, }, { name: "BiggsEXP5", p: Problem{ Func: functions.BiggsEXP5{}.Func, Grad: functions.BiggsEXP5{}.Grad, }, x: []float64{1, 2, 1, 1, 1}, gradTol: 1e-10, }, { name: "BiggsEXP5", p: Problem{ Func: functions.BiggsEXP5{}.Func, Grad: functions.BiggsEXP5{}.Grad, }, x: []float64{1.00001, 10.00001, 1.00001, 5.00001, 4.00001}, }, { name: "BiggsEXP6", p: Problem{ Func: functions.BiggsEXP6{}.Func, Grad: functions.BiggsEXP6{}.Grad, }, x: []float64{1, 2, 1, 1, 1, 1}, gradTol: 1e-8, }, { name: "BiggsEXP6", p: Problem{ Func: functions.BiggsEXP6{}.Func, Grad: functions.BiggsEXP6{}.Grad, }, x: []float64{1.00001, 10.00001, 1.00001, 5.00001, 4.00001, 3.00001}, gradTol: 1e-8, }, { name: "Box3D", p: Problem{ Func: functions.Box3D{}.Func, Grad: functions.Box3D{}.Grad, }, x: []float64{0, 10, 20}, }, { name: "Box3D", p: Problem{ Func: functions.Box3D{}.Func, Grad: functions.Box3D{}.Grad, }, x: []float64{1.00001, 10.00001, 1.00001}, }, { name: "Box3D", p: Problem{ Func: functions.Box3D{}.Func, Grad: functions.Box3D{}.Grad, }, x: []float64{100.00001, 100.00001, 0.00001}, }, { name: "BrownBadlyScaled", p: Problem{ Func: functions.BrownBadlyScaled{}.Func, Grad: functions.BrownBadlyScaled{}.Grad, }, x: []float64{1, 1}, gradTol: 1e-9, }, { name: "BrownBadlyScaled", p: Problem{ Func: functions.BrownBadlyScaled{}.Func, Grad: functions.BrownBadlyScaled{}.Grad, }, x: []float64{1.000001e6, 2.01e-6}, }, { name: "ExtendedPowellSingular", p: Problem{ Func: functions.ExtendedPowellSingular{}.Func, Grad: functions.ExtendedPowellSingular{}.Grad, }, x: []float64{3, -1, 0, 3}, }, { name: "ExtendedPowellSingular", p: Problem{ Func: functions.ExtendedPowellSingular{}.Func, Grad: functions.ExtendedPowellSingular{}.Grad, }, x: []float64{0.00001, 0.00001, 0.00001, 0.00001}, }, { name: "ExtendedPowellSingular", p: Problem{ Func: functions.ExtendedPowellSingular{}.Func, Grad: functions.ExtendedPowellSingular{}.Grad, }, x: []float64{3, -1, 0, 3, 3, -1, 0, 3}, }, { name: "ExtendedPowellSingular", p: Problem{ Func: functions.ExtendedPowellSingular{}.Func, Grad: functions.ExtendedPowellSingular{}.Grad, }, x: []float64{0.00001, 0.00001, 0.00001, 0.00001, 0.00001, 0.00001, 0.00001, 0.00001}, }, { name: "ExtendedRosenbrock", p: Problem{ Func: functions.ExtendedRosenbrock{}.Func, Grad: functions.ExtendedRosenbrock{}.Grad, }, x: []float64{-1.2, 1, -1.2, 1}, }, { name: "ExtendedRosenbrock", p: Problem{ Func: functions.ExtendedRosenbrock{}.Func, Grad: functions.ExtendedRosenbrock{}.Grad, }, x: []float64{1.00001, 1.00001, 1.00001, 1.00001}, }, { name: "Gaussian", p: Problem{ Func: functions.Gaussian{}.Func, Grad: functions.Gaussian{}.Grad, }, x: []float64{0.4, 1, 0}, gradTol: 1e-11, }, { name: "GulfResearchAndDevelopment", p: Problem{ Func: functions.GulfResearchAndDevelopment{}.Func, Grad: functions.GulfResearchAndDevelopment{}.Grad, }, x: []float64{5, 2.5, 0.15}, }, { name: "GulfResearchAndDevelopment", p: Problem{ Func: functions.GulfResearchAndDevelopment{}.Func, Grad: functions.GulfResearchAndDevelopment{}.Grad, }, x: []float64{50.00001, 25.00001, 1.50001}, }, { name: "GulfResearchAndDevelopment", p: Problem{ Func: functions.GulfResearchAndDevelopment{}.Func, Grad: functions.GulfResearchAndDevelopment{}.Grad, }, x: []float64{99.89529, 60.61453, 9.16124}, }, { name: "GulfResearchAndDevelopment", p: Problem{ Func: functions.GulfResearchAndDevelopment{}.Func, Grad: functions.GulfResearchAndDevelopment{}.Grad, }, x: []float64{201.66258, 60.61633, 10.22489}, }, { name: "PenaltyI", p: Problem{ Func: functions.PenaltyI{}.Func, Grad: functions.PenaltyI{}.Grad, }, x: []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, }, { name: "PenaltyI", p: Problem{ Func: functions.PenaltyI{}.Func, Grad: functions.PenaltyI{}.Grad, }, x: []float64{0.250007, 0.250007, 0.250007, 0.250007}, gradTol: 1e-9, }, { name: "PenaltyI", p: Problem{ Func: functions.PenaltyI{}.Func, Grad: functions.PenaltyI{}.Grad, }, x: []float64{0.1581, 0.1581, 0.1581, 0.1581, 0.1581, 0.1581, 0.1581, 0.1581, 0.1581, 0.1581}, }, { name: "PenaltyII", p: Problem{ Func: functions.PenaltyII{}.Func, Grad: functions.PenaltyII{}.Grad, }, x: []float64{0.5, 0.5, 0.5, 0.5}, gradTol: 1e-10, }, { name: "PenaltyII", p: Problem{ Func: functions.PenaltyII{}.Func, Grad: functions.PenaltyII{}.Grad, }, x: []float64{0.19999, 0.19131, 0.4801, 0.51884}, gradTol: 1e-10, }, { name: "PenaltyII", p: Problem{ Func: functions.PenaltyII{}.Func, Grad: functions.PenaltyII{}.Grad, }, x: []float64{0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5}, gradTol: 1e-9, }, { name: "PenaltyII", p: Problem{ Func: functions.PenaltyII{}.Func, Grad: functions.PenaltyII{}.Grad, }, x: []float64{0.19998, 0.01035, 0.01960, 0.03208, 0.04993, 0.07651, 0.11862, 0.19214, 0.34732, 0.36916}, gradTol: 1e-9, }, { name: "PowellBadlyScaled", p: Problem{ Func: functions.PowellBadlyScaled{}.Func, Grad: functions.PowellBadlyScaled{}.Grad, }, x: []float64{0, 1}, gradTol: 1e-10, }, { name: "PowellBadlyScaled", p: Problem{ Func: functions.PowellBadlyScaled{}.Func, Grad: functions.PowellBadlyScaled{}.Grad, }, x: []float64{1.09815e-05, 9.10614}, gradTol: 1e-10, }, newVariablyDimensioned(100, 1e-10), { name: "Watson", p: Problem{ Func: functions.Watson{}.Func, Grad: functions.Watson{}.Grad, }, x: []float64{0, 0, 0, 0, 0, 0}, gradTol: 1e-7, }, { name: "Watson", p: Problem{ Func: functions.Watson{}.Func, Grad: functions.Watson{}.Grad, }, x: []float64{-0.01572, 1.01243, -0.23299, 1.26043, -1.51372, 0.99299}, gradTol: 1e-7, }, { name: "Watson", p: Problem{ Func: functions.Watson{}.Func, Grad: functions.Watson{}.Grad, }, x: []float64{0, 0, 0, 0, 0, 0, 0, 0, 0}, gradTol: 1e-8, }, { name: "Watson", p: Problem{ Func: functions.Watson{}.Func, Grad: functions.Watson{}.Grad, }, x: []float64{-1.53070e-05, 0.99978, 0.01476, 0.14634, 1.00082, -2.61773, 4.10440, -3.14361, 1.05262}, gradTol: 1e-8, }, } var bfgsTests = []unconstrainedTest{ { name: "BiggsEXP6", p: Problem{ Func: functions.BiggsEXP6{}.Func, Grad: functions.BiggsEXP6{}.Grad, }, x: []float64{1, 2, 1, 1, 1, 1}, gradTol: 1e-10, }, { name: "BiggsEXP6", p: Problem{ Func: functions.BiggsEXP6{}.Func, Grad: functions.BiggsEXP6{}.Grad, }, x: []float64{1.00001, 10.00001, 1.00001, 5.00001, 4.00001, 3.00001}, gradTol: 1e-10, }, { name: "BrownAndDennis", p: Problem{ Func: functions.BrownAndDennis{}.Func, Grad: functions.BrownAndDennis{}.Grad, }, x: []float64{25, 5, -5, -1}, gradTol: 1e-3, }, { name: "ExtendedRosenbrock", p: Problem{ Func: functions.ExtendedRosenbrock{}.Func, Grad: functions.ExtendedRosenbrock{}.Grad, }, x: []float64{1e5, 1e5}, gradTol: 1e-10, }, { name: "Gaussian", p: Problem{ Func: functions.Gaussian{}.Func, Grad: functions.Gaussian{}.Grad, }, x: []float64{0.398, 1, 0}, gradTol: 1e-11, }, { name: "Wood", p: Problem{ Func: functions.Wood{}.Func, Grad: functions.Wood{}.Grad, }, x: []float64{-3, -1, -3, -1}, }, } var lbfgsTests = []unconstrainedTest{ { name: "BiggsEXP6", p: Problem{ Func: functions.BiggsEXP6{}.Func, Grad: functions.BiggsEXP6{}.Grad, }, x: []float64{1, 2, 1, 1, 1, 1}, gradTol: 1e-8, }, { name: "BiggsEXP6", p: Problem{ Func: functions.BiggsEXP6{}.Func, Grad: functions.BiggsEXP6{}.Grad, }, x: []float64{1.00001, 10.00001, 1.00001, 5.00001, 4.00001, 3.00001}, gradTol: 1e-8, }, { name: "ExtendedRosenbrock", p: Problem{ Func: functions.ExtendedRosenbrock{}.Func, Grad: functions.ExtendedRosenbrock{}.Grad, }, x: []float64{1e7, 1e6}, gradTol: 1e-10, }, { name: "Gaussian", p: Problem{ Func: functions.Gaussian{}.Func, Grad: functions.Gaussian{}.Grad, }, x: []float64{0.398, 1, 0}, gradTol: 1e-10, }, newVariablyDimensioned(1000, 1e-8), newVariablyDimensioned(10000, 1e-5), } var newtonTests = []unconstrainedTest{ { name: "Beale", p: Problem{ Func: functions.Beale{}.Func, Grad: functions.Beale{}.Grad, Hess: functions.Beale{}.Hess, }, x: []float64{1, 1}, }, { name: "BrownAndDennis", p: Problem{ Func: functions.BrownAndDennis{}.Func, Grad: functions.BrownAndDennis{}.Grad, Hess: functions.BrownAndDennis{}.Hess, }, x: []float64{25, 5, -5, -1}, gradTol: 1e-10, }, { name: "BrownBadlyScaled", p: Problem{ Func: functions.BrownBadlyScaled{}.Func, Grad: functions.BrownBadlyScaled{}.Grad, Hess: functions.BrownBadlyScaled{}.Hess, }, x: []float64{1, 1}, gradTol: 1e-9, }, { name: "PowellBadlyScaled", p: Problem{ Func: functions.PowellBadlyScaled{}.Func, Grad: functions.PowellBadlyScaled{}.Grad, Hess: functions.PowellBadlyScaled{}.Hess, }, x: []float64{0, 1}, gradTol: 1e-10, }, { name: "Watson", p: Problem{ Func: functions.Watson{}.Func, Grad: functions.Watson{}.Grad, Hess: functions.Watson{}.Hess, }, x: []float64{0, 0, 0, 0, 0, 0}, }, { name: "Watson", p: Problem{ Func: functions.Watson{}.Func, Grad: functions.Watson{}.Grad, Hess: functions.Watson{}.Hess, }, x: []float64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, }, { name: "Wood", p: Problem{ Func: functions.Wood{}.Func, Grad: functions.Wood{}.Grad, Hess: functions.Wood{}.Hess, }, x: []float64{-3, -1, -3, -1}, }, } func newVariablyDimensioned(dim int, gradTol float64) unconstrainedTest { x := make([]float64, dim) for i := range x { x[i] = float64(dim-i-1) / float64(dim) } return unconstrainedTest{ name: "VariablyDimensioned", p: Problem{ Func: functions.VariablyDimensioned{}.Func, Grad: functions.VariablyDimensioned{}.Grad, }, x: x, gradTol: gradTol, } } func TestLocal(t *testing.T) { t.Parallel() var tests []unconstrainedTest // Mix of functions with and without Grad method. tests = append(tests, gradFreeTests...) tests = append(tests, gradientDescentTests...) testLocal(t, tests, nil) } func TestNelderMead(t *testing.T) { t.Parallel() var tests []unconstrainedTest // Mix of functions with and without Grad method. tests = append(tests, gradFreeTests...) tests = append(tests, gradientDescentTests...) testLocal(t, tests, &NelderMead{}) } func TestGradientDescent(t *testing.T) { t.Parallel() testLocal(t, gradientDescentTests, &GradientDescent{}) } func TestGradientDescentBacktracking(t *testing.T) { t.Parallel() testLocal(t, gradientDescentTests, &GradientDescent{ Linesearcher: &Backtracking{ DecreaseFactor: 0.1, }, }) } func TestGradientDescentBisection(t *testing.T) { t.Parallel() testLocal(t, gradientDescentTests, &GradientDescent{ Linesearcher: &Bisection{}, }) } func TestCG(t *testing.T) { t.Parallel() var tests []unconstrainedTest tests = append(tests, gradientDescentTests...) tests = append(tests, cgTests...) testLocal(t, tests, &CG{}) } func TestFletcherReevesQuadStep(t *testing.T) { t.Parallel() var tests []unconstrainedTest tests = append(tests, gradientDescentTests...) tests = append(tests, cgTests...) testLocal(t, tests, &CG{ Variant: &FletcherReeves{}, InitialStep: &QuadraticStepSize{}, }) } func TestFletcherReevesFirstOrderStep(t *testing.T) { t.Parallel() var tests []unconstrainedTest tests = append(tests, gradientDescentTests...) tests = append(tests, cgTests...) testLocal(t, tests, &CG{ Variant: &FletcherReeves{}, InitialStep: &FirstOrderStepSize{}, }) } func TestHestenesStiefelQuadStep(t *testing.T) { t.Parallel() var tests []unconstrainedTest tests = append(tests, gradientDescentTests...) tests = append(tests, cgTests...) testLocal(t, tests, &CG{ Variant: &HestenesStiefel{}, InitialStep: &QuadraticStepSize{}, }) } func TestHestenesStiefelFirstOrderStep(t *testing.T) { t.Parallel() var tests []unconstrainedTest tests = append(tests, gradientDescentTests...) tests = append(tests, cgTests...) testLocal(t, tests, &CG{ Variant: &HestenesStiefel{}, InitialStep: &FirstOrderStepSize{}, }) } func TestPolakRibiereQuadStep(t *testing.T) { t.Parallel() var tests []unconstrainedTest tests = append(tests, gradientDescentTests...) tests = append(tests, cgTests...) testLocal(t, tests, &CG{ Variant: &PolakRibierePolyak{}, InitialStep: &QuadraticStepSize{}, }) } func TestPolakRibiereFirstOrderStep(t *testing.T) { t.Parallel() var tests []unconstrainedTest tests = append(tests, gradientDescentTests...) tests = append(tests, cgTests...) testLocal(t, tests, &CG{ Variant: &PolakRibierePolyak{}, InitialStep: &FirstOrderStepSize{}, }) } func TestDaiYuanQuadStep(t *testing.T) { t.Parallel() var tests []unconstrainedTest tests = append(tests, gradientDescentTests...) tests = append(tests, cgTests...) testLocal(t, tests, &CG{ Variant: &DaiYuan{}, InitialStep: &QuadraticStepSize{}, }) } func TestDaiYuanFirstOrderStep(t *testing.T) { t.Parallel() var tests []unconstrainedTest tests = append(tests, gradientDescentTests...) tests = append(tests, cgTests...) testLocal(t, tests, &CG{ Variant: &DaiYuan{}, InitialStep: &FirstOrderStepSize{}, }) } func TestHagerZhangQuadStep(t *testing.T) { t.Parallel() var tests []unconstrainedTest tests = append(tests, gradientDescentTests...) tests = append(tests, cgTests...) testLocal(t, tests, &CG{ Variant: &HagerZhang{}, InitialStep: &QuadraticStepSize{}, }) } func TestHagerZhangFirstOrderStep(t *testing.T) { t.Parallel() var tests []unconstrainedTest tests = append(tests, gradientDescentTests...) tests = append(tests, cgTests...) testLocal(t, tests, &CG{ Variant: &HagerZhang{}, InitialStep: &FirstOrderStepSize{}, }) } func TestBFGS(t *testing.T) { t.Parallel() var tests []unconstrainedTest tests = append(tests, gradientDescentTests...) tests = append(tests, quasiNewtonTests...) tests = append(tests, bfgsTests...) testLocal(t, tests, &BFGS{}) } func TestLBFGS(t *testing.T) { t.Parallel() var tests []unconstrainedTest tests = append(tests, gradientDescentTests...) tests = append(tests, quasiNewtonTests...) tests = append(tests, lbfgsTests...) testLocal(t, tests, &LBFGS{}) } func TestNewton(t *testing.T) { t.Parallel() testLocal(t, newtonTests, &Newton{}) } func testLocal(t *testing.T, tests []unconstrainedTest, method Method) { for cas, test := range tests { if test.long && testing.Short() { continue } settings := &Settings{} settings.Converger = defaultFunctionConverge() var uses Available if method != nil { var err error has := availFromProblem(test.p) uses, err = method.Uses(has) if err != nil { t.Errorf("problem and method mismatch: %v", err) continue } } if method != nil { // Turn off function convergence checks for gradient-based methods. if uses.Grad { settings.Converger = NeverTerminate{} } } else { if test.fIter == 0 { test.fIter = 20 } c := settings.Converger.(*FunctionConverge) c.Iterations = test.fIter if test.fAbsTol == 0 { test.fAbsTol = 1e-12 } c.Absolute = test.fAbsTol settings.Converger = c } if test.gradTol == 0 { test.gradTol = 1e-12 } settings.GradientThreshold = test.gradTol result, err := Minimize(test.p, test.x, settings, method) if err != nil { t.Errorf("Case %d: error finding minimum (%v) for:\n%v", cas, err, test) continue } if result == nil { t.Errorf("Case %d: nil result without error for:\n%v", cas, test) continue } // Check that the function value at the found optimum location is // equal to result.F. optF := test.p.Func(result.X) if optF != result.F { t.Errorf("Case %d: Function value at the optimum location %v not equal to the returned value %v for:\n%v", cas, optF, result.F, test) } if result.Gradient != nil { // Evaluate the norm of the gradient at the found optimum location. g := make([]float64, len(test.x)) test.p.Grad(g, result.X) if !floats.Equal(result.Gradient, g) { t.Errorf("Case %d: Gradient at the optimum location not equal to the returned value for:\n%v", cas, test) } optNorm := floats.Norm(g, math.Inf(1)) // Check that the norm of the gradient at the found optimum location is // smaller than the tolerance. if optNorm >= settings.GradientThreshold { t.Errorf("Case %d: Norm of the gradient at the optimum location %v not smaller than tolerance %v for:\n%v", cas, optNorm, settings.GradientThreshold, test) } } if method == nil { // The tests below make sense only if the method used is known. continue } if !uses.Grad && !uses.Hess { // Gradient-free tests can correctly terminate only with // FunctionConvergence status. if result.Status != FunctionConvergence { t.Errorf("Status not %v, %v instead", FunctionConvergence, result.Status) } } // We are going to restart the solution using known initial data, so // evaluate them. settings.InitValues = &Location{} settings.InitValues.F = test.p.Func(test.x) if uses.Grad { settings.InitValues.Gradient = resize(settings.InitValues.Gradient, len(test.x)) test.p.Grad(settings.InitValues.Gradient, test.x) } if uses.Hess { settings.InitValues.Hessian = mat.NewSymDense(len(test.x), nil) test.p.Hess(settings.InitValues.Hessian, test.x) } // Rerun the test again to make sure that it gets the same answer with // the same starting condition. Moreover, we are using the initial data. result2, err2 := Minimize(test.p, test.x, settings, method) if err2 != nil { t.Errorf("error finding minimum second time (%v) for:\n%v", err2, test) continue } if result2 == nil { t.Errorf("second time nil result without error for:\n%v", test) continue } // At the moment all the optimizers are deterministic, so check that we // get _exactly_ the same answer second time as well. if result.F != result2.F || !floats.Equal(result.X, result2.X) { t.Errorf("Different minimum second time for:\n%v", test) } // Check that providing initial data reduces the number of evaluations exactly by one. if result.FuncEvaluations != result2.FuncEvaluations+1 { t.Errorf("Providing initial data does not reduce the number of Func calls for:\n%v", test) continue } if uses.Grad { if result.GradEvaluations != result2.GradEvaluations+1 { t.Errorf("Providing initial data does not reduce the number of Grad calls for:\n%v", test) continue } } if uses.Hess { if result.HessEvaluations != result2.HessEvaluations+1 { t.Errorf("Providing initial data does not reduce the number of Hess calls for:\n%v", test) continue } } } } func TestIssue76(t *testing.T) { t.Parallel() p := Problem{ Func: functions.BrownAndDennis{}.Func, Grad: functions.BrownAndDennis{}.Grad, } // Location very close to the minimum. x := []float64{-11.594439904886773, 13.203630051265385, -0.40343948776868443, 0.2367787746745986} s := &Settings{ MajorIterations: 1000000, } m := &GradientDescent{ GradStopThreshold: 1e-14, Linesearcher: &Backtracking{}, } // We are not interested in the error, only in the returned status. r, _ := Minimize(p, x, s, m) // With the above stringent tolerance, the optimizer will never // successfully reach the minimum. Check if it terminated in a finite // number of steps. if r.Status == IterationLimit { t.Error("Issue https://github.com/gonum/optimize/issues/76 not fixed") } } func TestNelderMeadOneD(t *testing.T) { t.Parallel() p := Problem{ Func: func(x []float64) float64 { return x[0] * x[0] }, } x := []float64{10} m := &NelderMead{} var s *Settings result, err := Minimize(p, x, s, m) if err != nil { t.Errorf(err.Error()) } if !floats.EqualApprox(result.X, []float64{0}, 1e-10) { t.Errorf("Minimum not found") } if m.reflection != 1 { t.Errorf("Wrong value of reflection") } if m.expansion != 2 { t.Errorf("Wrong value of expansion") } if m.contraction != 0.5 { t.Errorf("Wrong value of contraction") } if m.shrink != 0.5 { t.Errorf("Wrong value of shrink") } }