github.com/gopherd/gonum@v0.0.4/spatial/r3/mat_test.go (about)

     1  // Copyright ©2021 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 r3
     6  
     7  import (
     8  	"math"
     9  	"testing"
    10  
    11  	"math/rand"
    12  
    13  	"github.com/gopherd/gonum/mat"
    14  	"github.com/gopherd/gonum/num/quat"
    15  )
    16  
    17  func TestMatAdd(t *testing.T) {
    18  	const tol = 1e-16
    19  	rnd := rand.New(rand.NewSource(1))
    20  	for tc := 0; tc < 20; tc++ {
    21  		a := randomMat(rnd)
    22  		b := randomMat(rnd)
    23  		var (
    24  			want mat.Dense
    25  			got  Mat
    26  		)
    27  		want.Add(a, b)
    28  		got.Add(a, b)
    29  		if !mat.EqualApprox(&got, &want, tol) {
    30  			t.Errorf("unexpected result for matrix add:\ngot:\n%v\nwant:\n%v", mat.Formatted(&got), mat.Formatted(&want))
    31  		}
    32  	}
    33  }
    34  
    35  func TestMatSub(t *testing.T) {
    36  	const tol = 1e-16
    37  	rnd := rand.New(rand.NewSource(1))
    38  	for tc := 0; tc < 20; tc++ {
    39  		a := randomMat(rnd)
    40  		b := randomMat(rnd)
    41  		var (
    42  			want mat.Dense
    43  			got  Mat
    44  		)
    45  		want.Sub(a, b)
    46  		got.Sub(a, b)
    47  		if !mat.EqualApprox(&got, &want, tol) {
    48  			t.Errorf("unexpected result for matrix subtract:\ngot:\n%v\nwant:\n%v", mat.Formatted(&got), mat.Formatted(&want))
    49  		}
    50  	}
    51  }
    52  
    53  func TestMatMul(t *testing.T) {
    54  	const tol = 1e-14
    55  	rnd := rand.New(rand.NewSource(1))
    56  	for tc := 0; tc < 20; tc++ {
    57  		a := randomMat(rnd)
    58  		b := randomMat(rnd)
    59  		var (
    60  			want mat.Dense
    61  			got  Mat
    62  		)
    63  		want.Mul(a, b)
    64  		got.Mul(a, b)
    65  		if !mat.EqualApprox(&got, &want, tol) {
    66  			t.Errorf("unexpected result for matrix multiply:\ngot:\n%v\nwant:\n%v", mat.Formatted(&got), mat.Formatted(&want))
    67  		}
    68  	}
    69  }
    70  
    71  func TestMatScale(t *testing.T) {
    72  	const tol = 1e-16
    73  	rnd := rand.New(rand.NewSource(1))
    74  	for tc := 0; tc < 20; tc++ {
    75  		v := rnd.Float64()
    76  		a := randomMat(rnd)
    77  		var (
    78  			want mat.Dense
    79  			got  Mat
    80  		)
    81  		want.Scale(v, a)
    82  		got.Scale(v, a)
    83  		if !mat.EqualApprox(&got, &want, tol) {
    84  			t.Errorf("unexpected result for matrix scale:\ngot:\n%v\nwant:\n%v", mat.Formatted(&got), mat.Formatted(&want))
    85  		}
    86  	}
    87  }
    88  
    89  func TestMatCloneFrom(t *testing.T) {
    90  	const tol = 1e-16
    91  	rnd := rand.New(rand.NewSource(1))
    92  	for tc := 0; tc < 20; tc++ {
    93  		want := randomMat(rnd)
    94  		got := NewMat(nil)
    95  		got.CloneFrom(want)
    96  		if !mat.EqualApprox(got, want, tol) {
    97  			t.Errorf("unexpected result from CloneFrom:\ngot:\n%v\nwant:\n%v", mat.Formatted(got), mat.Formatted(want))
    98  		}
    99  	}
   100  }
   101  
   102  func TestSkew(t *testing.T) {
   103  	const tol = 1e-16
   104  	rnd := rand.New(rand.NewSource(1))
   105  	for tc := 0; tc < 20; tc++ {
   106  		v1 := randomVec(rnd)
   107  		v2 := randomVec(rnd)
   108  		sk := Skew(v1)
   109  		want := Cross(v1, v2)
   110  		got := sk.MulVec(v2)
   111  		if d := want.Sub(got); d.Dot(d) > tol {
   112  			t.Errorf("r3.Cross(v1,v2) does not agree with r3.Skew(v1)*v2: got:%v want:%v", got, want)
   113  		}
   114  	}
   115  }
   116  
   117  func TestTranspose(t *testing.T) {
   118  	const tol = 1e-16
   119  	rnd := rand.New(rand.NewSource(1))
   120  	for tc := 0; tc < 20; tc++ {
   121  		d := mat.NewDense(3, 3, nil)
   122  		m := randomMat(rnd)
   123  		d.CloneFrom(m)
   124  		mt := m.T()
   125  		dt := d.T()
   126  		if !mat.Equal(mt, dt) {
   127  			t.Errorf("Dense.T() not equal to r3.Mat.T():\ngot:\n%v\nwant:\n%v", mat.Formatted(mt), mat.Formatted(dt))
   128  		}
   129  		vd := mat.NewVecDense(3, nil)
   130  		v := randomVec(rnd)
   131  		vd.SetVec(0, v.X)
   132  		vd.SetVec(1, v.Y)
   133  		vd.SetVec(2, v.Z)
   134  		vd.MulVec(dt, vd)
   135  		want := Vec{X: vd.AtVec(0), Y: vd.AtVec(1), Z: vd.AtVec(2)}
   136  		got := m.MulVecTrans(v)
   137  		if d := want.Sub(got); d.Dot(d) > tol {
   138  			t.Errorf("VecDense.MulVec(dense.T()) not agree with r3.Mat.MulVec(r3.Vec): got:%v want:%v", got, want)
   139  		}
   140  	}
   141  }
   142  
   143  func randomMat(rnd *rand.Rand) *Mat {
   144  	m := Mat{new(array)}
   145  	for iv := 0; iv < 9; iv++ {
   146  		i := iv / 3
   147  		j := iv % 3
   148  		m.Set(i, j, (rnd.Float64()-0.5)*20)
   149  	}
   150  	return &m
   151  }
   152  
   153  func randomVec(rnd *rand.Rand) (v Vec) {
   154  	v.X = (rnd.Float64() - 0.5) * 20
   155  	v.Y = (rnd.Float64() - 0.5) * 20
   156  	v.Z = (rnd.Float64() - 0.5) * 20
   157  	return v
   158  }
   159  
   160  func TestDet(t *testing.T) {
   161  	const tol = 1e-11
   162  	rnd := rand.New(rand.NewSource(1))
   163  	for tc := 0; tc < 20; tc++ {
   164  		m := randomMat(rnd)
   165  		got := m.Det()
   166  		want := mat.Det(m)
   167  		if math.Abs(got-want) > tol {
   168  			t.Errorf("r3.Mat.Det() not equal to mat.Det(). got %f, want %f", got, want)
   169  		}
   170  	}
   171  }
   172  
   173  func TestOuter(t *testing.T) {
   174  	rnd := rand.New(rand.NewSource(1))
   175  	for tc := 0; tc < 20; tc++ {
   176  		alpha := rnd.Float64()
   177  		d := mat.NewDense(3, 3, nil)
   178  		n := NewMat(nil)
   179  		v1 := randomVec(rnd)
   180  		v2 := randomVec(rnd)
   181  		d1 := mat.NewVecDense(3, []float64{v1.X, v1.Y, v1.Z})
   182  		d2 := mat.NewVecDense(3, []float64{v2.X, v2.Y, v2.Z})
   183  		d.Outer(alpha, d1, d2)
   184  		n.Outer(alpha, v1, v2)
   185  		if !mat.Equal(d, n) {
   186  			t.Error("matrices not equal")
   187  		}
   188  	}
   189  }
   190  
   191  func TestRotationMat(t *testing.T) {
   192  	const tol = 1e-14
   193  	rnd := rand.New(rand.NewSource(1))
   194  
   195  	for tc := 0; tc < 20; tc++ {
   196  		// Generate a random unit quaternion.
   197  		q := quat.Number{Real: rnd.NormFloat64(), Imag: rnd.NormFloat64(), Jmag: rnd.NormFloat64(), Kmag: rnd.NormFloat64()}
   198  		q = quat.Scale(1/quat.Abs(q), q)
   199  
   200  		// Convert it to a rotation matrix R.
   201  		r := Rotation(q).Mat()
   202  
   203  		// Check that the matrix has the determinant approximately equal to 1.
   204  		diff := math.Abs(r.Det() - 1)
   205  		if diff > tol {
   206  			t.Errorf("case %d: unexpected determinant of R; got=%f, want=1 (diff=%v)", tc, r.Det(), diff)
   207  			continue
   208  		}
   209  
   210  		// Generate a random point.
   211  		v := Vec{X: rnd.NormFloat64(), Y: rnd.NormFloat64(), Z: rnd.NormFloat64()}
   212  		// Rotate it using the formula q*v*conj(q).
   213  		want := Rotation(q).Rotate(v)
   214  		// Rotate it using the rotation matrix R.
   215  		got := r.MulVec(v)
   216  		// Check that |got-want| is small.
   217  		diff = Norm(Sub(got, want))
   218  		if diff > tol {
   219  			t.Errorf("case %d: unexpected result; got=%f, want=%f, (diff=%v)", tc, got, want, diff)
   220  		}
   221  	}
   222  }
   223  
   224  func BenchmarkQuat(b *testing.B) {
   225  	rnd := rand.New(rand.NewSource(1))
   226  	for i := 0; i < b.N; i++ {
   227  		q := quat.Number{Real: rnd.Float64(), Imag: rnd.Float64(), Jmag: rnd.Float64(), Kmag: rnd.Float64()}
   228  		if Rotation(q).Mat() == nil {
   229  			b.Fatal("nil return")
   230  		}
   231  	}
   232  }