github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/diff/fd/jacobian_test.go (about) 1 // Copyright ©2016 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 fd 6 7 import ( 8 "math" 9 "testing" 10 11 "golang.org/x/exp/rand" 12 13 "github.com/jingcheng-WU/gonum/floats" 14 "github.com/jingcheng-WU/gonum/mat" 15 ) 16 17 func vecFunc13(y, x []float64) { 18 y[0] = 5*x[0] + x[2]*math.Sin(x[1]) + 1 19 } 20 func vecFunc13Jac(jac *mat.Dense, x []float64) { 21 jac.Set(0, 0, 5) 22 jac.Set(0, 1, x[2]*math.Cos(x[1])) 23 jac.Set(0, 2, math.Sin(x[1])) 24 } 25 26 func vecFunc22(y, x []float64) { 27 y[0] = x[0]*x[0]*x[1] + 1 28 y[1] = 5*x[0] + math.Sin(x[1]) + 1 29 } 30 func vecFunc22Jac(jac *mat.Dense, x []float64) { 31 jac.Set(0, 0, 2*x[0]*x[1]) 32 jac.Set(0, 1, x[0]*x[0]) 33 jac.Set(1, 0, 5) 34 jac.Set(1, 1, math.Cos(x[1])) 35 } 36 37 func vecFunc43(y, x []float64) { 38 y[0] = x[0] + 1 39 y[1] = 5*x[2] + 1 40 y[2] = 4*x[1]*x[1] - 2*x[2] + 1 41 y[3] = x[2]*math.Sin(x[0]) + 1 42 } 43 func vecFunc43Jac(jac *mat.Dense, x []float64) { 44 jac.Set(0, 0, 1) 45 jac.Set(0, 1, 0) 46 jac.Set(0, 2, 0) 47 jac.Set(1, 0, 0) 48 jac.Set(1, 1, 0) 49 jac.Set(1, 2, 5) 50 jac.Set(2, 0, 0) 51 jac.Set(2, 1, 8*x[1]) 52 jac.Set(2, 2, -2) 53 jac.Set(3, 0, x[2]*math.Cos(x[0])) 54 jac.Set(3, 1, 0) 55 jac.Set(3, 2, math.Sin(x[0])) 56 } 57 58 func TestJacobian(t *testing.T) { 59 t.Parallel() 60 rnd := rand.New(rand.NewSource(1)) 61 62 // Test with default settings. 63 for tc, test := range []struct { 64 m, n int 65 f func([]float64, []float64) 66 jac func(*mat.Dense, []float64) 67 }{ 68 { 69 m: 1, 70 n: 3, 71 f: vecFunc13, 72 jac: vecFunc13Jac, 73 }, 74 { 75 m: 2, 76 n: 2, 77 f: vecFunc22, 78 jac: vecFunc22Jac, 79 }, 80 { 81 m: 4, 82 n: 3, 83 f: vecFunc43, 84 jac: vecFunc43Jac, 85 }, 86 } { 87 const tol = 1e-6 88 89 x := randomSlice(rnd, test.n, 10) 90 xcopy := make([]float64, test.n) 91 copy(xcopy, x) 92 93 want := mat.NewDense(test.m, test.n, nil) 94 test.jac(want, x) 95 96 got := mat.NewDense(test.m, test.n, nil) 97 fillNaNDense(got) 98 Jacobian(got, test.f, x, nil) 99 if !mat.EqualApprox(want, got, tol) { 100 t.Errorf("Case %d (default settings): unexpected Jacobian.\nwant: %v\ngot: %v", 101 tc, mat.Formatted(want, mat.Prefix(" ")), mat.Formatted(got, mat.Prefix(" "))) 102 } 103 if !floats.Equal(x, xcopy) { 104 t.Errorf("Case %d (default settings): x modified", tc) 105 } 106 } 107 108 // Test with non-default settings. 109 for tc, test := range []struct { 110 m, n int 111 f func([]float64, []float64) 112 jac func(*mat.Dense, []float64) 113 tol float64 114 formula Formula 115 }{ 116 { 117 m: 1, 118 n: 3, 119 f: vecFunc13, 120 jac: vecFunc13Jac, 121 tol: 1e-6, 122 formula: Forward, 123 }, 124 { 125 m: 1, 126 n: 3, 127 f: vecFunc13, 128 jac: vecFunc13Jac, 129 tol: 1e-6, 130 formula: Backward, 131 }, 132 { 133 m: 1, 134 n: 3, 135 f: vecFunc13, 136 jac: vecFunc13Jac, 137 tol: 1e-9, 138 formula: Central, 139 }, 140 { 141 m: 2, 142 n: 2, 143 f: vecFunc22, 144 jac: vecFunc22Jac, 145 tol: 1e-6, 146 formula: Forward, 147 }, 148 { 149 m: 2, 150 n: 2, 151 f: vecFunc22, 152 jac: vecFunc22Jac, 153 tol: 1e-6, 154 formula: Backward, 155 }, 156 { 157 m: 2, 158 n: 2, 159 f: vecFunc22, 160 jac: vecFunc22Jac, 161 tol: 1e-9, 162 formula: Central, 163 }, 164 { 165 m: 4, 166 n: 3, 167 f: vecFunc43, 168 jac: vecFunc43Jac, 169 tol: 1e-6, 170 formula: Forward, 171 }, 172 { 173 m: 4, 174 n: 3, 175 f: vecFunc43, 176 jac: vecFunc43Jac, 177 tol: 1e-6, 178 formula: Backward, 179 }, 180 { 181 m: 4, 182 n: 3, 183 f: vecFunc43, 184 jac: vecFunc43Jac, 185 tol: 1e-9, 186 formula: Central, 187 }, 188 } { 189 x := randomSlice(rnd, test.n, 10) 190 xcopy := make([]float64, test.n) 191 copy(xcopy, x) 192 193 want := mat.NewDense(test.m, test.n, nil) 194 test.jac(want, x) 195 196 got := mat.NewDense(test.m, test.n, nil) 197 fillNaNDense(got) 198 Jacobian(got, test.f, x, &JacobianSettings{ 199 Formula: test.formula, 200 }) 201 if !mat.EqualApprox(want, got, test.tol) { 202 t.Errorf("Case %d: unexpected Jacobian.\nwant: %v\ngot: %v", 203 tc, mat.Formatted(want, mat.Prefix(" ")), mat.Formatted(got, mat.Prefix(" "))) 204 } 205 if !floats.Equal(x, xcopy) { 206 t.Errorf("Case %d: x modified", tc) 207 } 208 209 fillNaNDense(got) 210 Jacobian(got, test.f, x, &JacobianSettings{ 211 Formula: test.formula, 212 Concurrent: true, 213 }) 214 if !mat.EqualApprox(want, got, test.tol) { 215 t.Errorf("Case %d (concurrent): unexpected Jacobian.\nwant: %v\ngot: %v", 216 tc, mat.Formatted(want, mat.Prefix(" ")), mat.Formatted(got, mat.Prefix(" "))) 217 } 218 if !floats.Equal(x, xcopy) { 219 t.Errorf("Case %d (concurrent): x modified", tc) 220 } 221 222 fillNaNDense(got) 223 origin := make([]float64, test.m) 224 test.f(origin, x) 225 Jacobian(got, test.f, x, &JacobianSettings{ 226 Formula: test.formula, 227 OriginValue: origin, 228 }) 229 if !mat.EqualApprox(want, got, test.tol) { 230 t.Errorf("Case %d (origin): unexpected Jacobian.\nwant: %v\ngot: %v", 231 tc, mat.Formatted(want, mat.Prefix(" ")), mat.Formatted(got, mat.Prefix(" "))) 232 } 233 if !floats.Equal(x, xcopy) { 234 t.Errorf("Case %d (origin): x modified", tc) 235 } 236 237 fillNaNDense(got) 238 Jacobian(got, test.f, x, &JacobianSettings{ 239 Formula: test.formula, 240 OriginValue: origin, 241 Concurrent: true, 242 }) 243 if !mat.EqualApprox(want, got, test.tol) { 244 t.Errorf("Case %d (concurrent, origin): unexpected Jacobian.\nwant: %v\ngot: %v", 245 tc, mat.Formatted(want, mat.Prefix(" ")), mat.Formatted(got, mat.Prefix(" "))) 246 } 247 if !floats.Equal(x, xcopy) { 248 t.Errorf("Case %d (concurrent, origin): x modified", tc) 249 } 250 } 251 } 252 253 // randomSlice returns a slice of n elements from the interval [-bound,bound). 254 func randomSlice(rnd *rand.Rand, n int, bound float64) []float64 { 255 x := make([]float64, n) 256 for i := range x { 257 x[i] = 2*bound*rnd.Float64() - bound 258 } 259 return x 260 } 261 262 // fillNaNDense fills the matrix m with NaN values. 263 func fillNaNDense(m *mat.Dense) { 264 r, c := m.Dims() 265 for i := 0; i < r; i++ { 266 for j := 0; j < c; j++ { 267 m.Set(i, j, math.NaN()) 268 } 269 } 270 }