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  }