gonum.org/v1/gonum@v0.14.0/spatial/r3/mat.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 "gonum.org/v1/gonum/mat"
     8  
     9  // Mat represents a 3×3 matrix. Useful for rotation matrices and such.
    10  // The zero value is usable as the 3×3 zero matrix.
    11  type Mat struct {
    12  	data *array
    13  }
    14  
    15  var _ mat.Matrix = (*Mat)(nil)
    16  
    17  // NewMat returns a new 3×3 matrix Mat type and populates its elements
    18  // with values passed as argument in row-major form. If val argument
    19  // is nil then NewMat returns a matrix filled with zeros.
    20  func NewMat(val []float64) *Mat {
    21  	if len(val) == 9 {
    22  		return &Mat{arrayFrom(val)}
    23  	}
    24  	if val == nil {
    25  		return &Mat{new(array)}
    26  	}
    27  	panic(mat.ErrShape)
    28  }
    29  
    30  // Dims returns the number of rows and columns of this matrix.
    31  // This method will always return 3×3 for a Mat.
    32  func (m *Mat) Dims() (r, c int) { return 3, 3 }
    33  
    34  // T returns the transpose of Mat. Changes in the receiver will be reflected in the returned matrix.
    35  func (m *Mat) T() mat.Matrix { return mat.Transpose{Matrix: m} }
    36  
    37  // Scale multiplies the elements of a by f, placing the result in the receiver.
    38  //
    39  // See the mat.Scaler interface for more information.
    40  func (m *Mat) Scale(f float64, a mat.Matrix) {
    41  	r, c := a.Dims()
    42  	if r != 3 || c != 3 {
    43  		panic(mat.ErrShape)
    44  	}
    45  	if m.data == nil {
    46  		m.data = new(array)
    47  	}
    48  	for i := 0; i < 3; i++ {
    49  		for j := 0; j < 3; j++ {
    50  			m.Set(i, j, f*a.At(i, j))
    51  		}
    52  	}
    53  }
    54  
    55  // MulVec returns the matrix-vector product M⋅v.
    56  func (m *Mat) MulVec(v Vec) Vec {
    57  	if m.data == nil {
    58  		return Vec{}
    59  	}
    60  	return Vec{
    61  		X: v.X*m.At(0, 0) + v.Y*m.At(0, 1) + v.Z*m.At(0, 2),
    62  		Y: v.X*m.At(1, 0) + v.Y*m.At(1, 1) + v.Z*m.At(1, 2),
    63  		Z: v.X*m.At(2, 0) + v.Y*m.At(2, 1) + v.Z*m.At(2, 2),
    64  	}
    65  }
    66  
    67  // MulVecTrans returns the matrix-vector product Mᵀ⋅v.
    68  func (m *Mat) MulVecTrans(v Vec) Vec {
    69  	if m.data == nil {
    70  		return Vec{}
    71  	}
    72  	return Vec{
    73  		X: v.X*m.At(0, 0) + v.Y*m.At(1, 0) + v.Z*m.At(2, 0),
    74  		Y: v.X*m.At(0, 1) + v.Y*m.At(1, 1) + v.Z*m.At(2, 1),
    75  		Z: v.X*m.At(0, 2) + v.Y*m.At(1, 2) + v.Z*m.At(2, 2),
    76  	}
    77  }
    78  
    79  // CloneFrom makes a copy of a into the receiver m.
    80  // Mat expects a 3×3 input matrix.
    81  func (m *Mat) CloneFrom(a mat.Matrix) {
    82  	r, c := a.Dims()
    83  	if r != 3 || c != 3 {
    84  		panic(mat.ErrShape)
    85  	}
    86  	if m.data == nil {
    87  		m.data = new(array)
    88  	}
    89  	for i := 0; i < 3; i++ {
    90  		for j := 0; j < 3; j++ {
    91  			m.Set(i, j, a.At(i, j))
    92  		}
    93  	}
    94  }
    95  
    96  // Sub subtracts the matrix b from a, placing the result in the receiver.
    97  // Sub will panic if the two matrices do not have the same shape.
    98  func (m *Mat) Sub(a, b mat.Matrix) {
    99  	if r, c := a.Dims(); r != 3 || c != 3 {
   100  		panic(mat.ErrShape)
   101  	}
   102  	if r, c := b.Dims(); r != 3 || c != 3 {
   103  		panic(mat.ErrShape)
   104  	}
   105  	if m.data == nil {
   106  		m.data = new(array)
   107  	}
   108  
   109  	m.Set(0, 0, a.At(0, 0)-b.At(0, 0))
   110  	m.Set(0, 1, a.At(0, 1)-b.At(0, 1))
   111  	m.Set(0, 2, a.At(0, 2)-b.At(0, 2))
   112  	m.Set(1, 0, a.At(1, 0)-b.At(1, 0))
   113  	m.Set(1, 1, a.At(1, 1)-b.At(1, 1))
   114  	m.Set(1, 2, a.At(1, 2)-b.At(1, 2))
   115  	m.Set(2, 0, a.At(2, 0)-b.At(2, 0))
   116  	m.Set(2, 1, a.At(2, 1)-b.At(2, 1))
   117  	m.Set(2, 2, a.At(2, 2)-b.At(2, 2))
   118  }
   119  
   120  // Add adds a and b element-wise, placing the result in the receiver. Add will panic if the two matrices do not have the same shape.
   121  func (m *Mat) Add(a, b mat.Matrix) {
   122  	if r, c := a.Dims(); r != 3 || c != 3 {
   123  		panic(mat.ErrShape)
   124  	}
   125  	if r, c := b.Dims(); r != 3 || c != 3 {
   126  		panic(mat.ErrShape)
   127  	}
   128  	if m.data == nil {
   129  		m.data = new(array)
   130  	}
   131  
   132  	m.Set(0, 0, a.At(0, 0)+b.At(0, 0))
   133  	m.Set(0, 1, a.At(0, 1)+b.At(0, 1))
   134  	m.Set(0, 2, a.At(0, 2)+b.At(0, 2))
   135  	m.Set(1, 0, a.At(1, 0)+b.At(1, 0))
   136  	m.Set(1, 1, a.At(1, 1)+b.At(1, 1))
   137  	m.Set(1, 2, a.At(1, 2)+b.At(1, 2))
   138  	m.Set(2, 0, a.At(2, 0)+b.At(2, 0))
   139  	m.Set(2, 1, a.At(2, 1)+b.At(2, 1))
   140  	m.Set(2, 2, a.At(2, 2)+b.At(2, 2))
   141  }
   142  
   143  // VecRow returns the elements in the ith row of the receiver.
   144  func (m *Mat) VecRow(i int) Vec {
   145  	if i > 2 {
   146  		panic(mat.ErrRowAccess)
   147  	}
   148  	if m.data == nil {
   149  		return Vec{}
   150  	}
   151  	return Vec{X: m.At(i, 0), Y: m.At(i, 1), Z: m.At(i, 2)}
   152  }
   153  
   154  // VecCol returns the elements in the jth column of the receiver.
   155  func (m *Mat) VecCol(j int) Vec {
   156  	if j > 2 {
   157  		panic(mat.ErrColAccess)
   158  	}
   159  	if m.data == nil {
   160  		return Vec{}
   161  	}
   162  	return Vec{X: m.At(0, j), Y: m.At(1, j), Z: m.At(2, j)}
   163  }
   164  
   165  // Outer calculates the outer product of the vectors x and y,
   166  // where x and y are treated as column vectors, and stores the result in the receiver.
   167  //
   168  //	m = alpha * x * yᵀ
   169  func (m *Mat) Outer(alpha float64, x, y Vec) {
   170  	ax := alpha * x.X
   171  	ay := alpha * x.Y
   172  	az := alpha * x.Z
   173  	m.Set(0, 0, ax*y.X)
   174  	m.Set(0, 1, ax*y.Y)
   175  	m.Set(0, 2, ax*y.Z)
   176  
   177  	m.Set(1, 0, ay*y.X)
   178  	m.Set(1, 1, ay*y.Y)
   179  	m.Set(1, 2, ay*y.Z)
   180  
   181  	m.Set(2, 0, az*y.X)
   182  	m.Set(2, 1, az*y.Y)
   183  	m.Set(2, 2, az*y.Z)
   184  }
   185  
   186  // Det calculates the determinant of the receiver using the following formula
   187  //
   188  //	    ⎡a b c⎤
   189  //	m = ⎢d e f⎥
   190  //	    ⎣g h i⎦
   191  //	det(m) = a(ei − fh) − b(di − fg) + c(dh − eg)
   192  func (m *Mat) Det() float64 {
   193  	a := m.At(0, 0)
   194  	b := m.At(0, 1)
   195  	c := m.At(0, 2)
   196  
   197  	deta := m.At(1, 1)*m.At(2, 2) - m.At(1, 2)*m.At(2, 1)
   198  	detb := m.At(1, 0)*m.At(2, 2) - m.At(1, 2)*m.At(2, 0)
   199  	detc := m.At(1, 0)*m.At(2, 1) - m.At(1, 1)*m.At(2, 0)
   200  	return a*deta - b*detb + c*detc
   201  }
   202  
   203  // Skew sets the receiver to the 3×3 skew symmetric matrix
   204  // (right hand system) of v.
   205  //
   206  //	                ⎡ 0 -z  y⎤
   207  //	Skew({x,y,z}) = ⎢ z  0 -x⎥
   208  //	                ⎣-y  x  0⎦
   209  func (m *Mat) Skew(v Vec) {
   210  	m.Set(0, 0, 0)
   211  	m.Set(0, 1, -v.Z)
   212  	m.Set(0, 2, v.Y)
   213  	m.Set(1, 0, v.Z)
   214  	m.Set(1, 1, 0)
   215  	m.Set(1, 2, -v.X)
   216  	m.Set(2, 0, -v.Y)
   217  	m.Set(2, 1, v.X)
   218  	m.Set(2, 2, 0)
   219  }
   220  
   221  // Hessian sets the receiver to the Hessian matrix of the scalar field at the point p,
   222  // approximated using finite differences with the given step sizes.
   223  // The field is evaluated at points in the area surrounding p by adding
   224  // at most 2 components of step to p. Hessian expects the field's second partial
   225  // derivatives are all continuous for correct results.
   226  func (m *Mat) Hessian(p, step Vec, field func(Vec) float64) {
   227  	dx := Vec{X: step.X}
   228  	dy := Vec{Y: step.Y}
   229  	dz := Vec{Z: step.Z}
   230  	fp := field(p)
   231  	fxp := field(Add(p, dx))
   232  	fxm := field(Sub(p, dx))
   233  	fxx := (fxp - 2*fp + fxm) / (step.X * step.X)
   234  
   235  	fyp := field(Add(p, dy))
   236  	fym := field(Sub(p, dy))
   237  	fyy := (fyp - 2*fp + fym) / (step.Y * step.Y)
   238  
   239  	aux := Add(dx, dy)
   240  	fxyp := field(Add(p, aux))
   241  	fxym := field(Sub(p, aux))
   242  	fxy := (fxyp - fxp - fyp + 2*fp - fxm - fym + fxym) / (2 * step.X * step.Y)
   243  
   244  	fzp := field(Add(p, dz))
   245  	fzm := field(Sub(p, dz))
   246  	fzz := (fzp - 2*fp + fzm) / (step.Z * step.Z)
   247  
   248  	aux = Add(dx, dz)
   249  	fxzp := field(Add(p, aux))
   250  	fxzm := field(Sub(p, aux))
   251  	fxz := (fxzp - fxp - fzp + 2*fp - fxm - fzm + fxzm) / (2 * step.X * step.Z)
   252  
   253  	aux = Add(dy, dz)
   254  	fyzp := field(Add(p, aux))
   255  	fyzm := field(Sub(p, aux))
   256  	fyz := (fyzp - fyp - fzp + 2*fp - fym - fzm + fyzm) / (2 * step.Y * step.Z)
   257  
   258  	m.Set(0, 0, fxx)
   259  	m.Set(0, 1, fxy)
   260  	m.Set(0, 2, fxz)
   261  
   262  	m.Set(1, 0, fxy)
   263  	m.Set(1, 1, fyy)
   264  	m.Set(1, 2, fyz)
   265  
   266  	m.Set(2, 0, fxz)
   267  	m.Set(2, 1, fyz)
   268  	m.Set(2, 2, fzz)
   269  }
   270  
   271  // Jacobian sets the receiver to the Jacobian matrix of the vector field at
   272  // the point p, approximated using finite differences with the given step sizes.
   273  //
   274  // The Jacobian matrix J is the matrix of all first-order partial derivatives of f.
   275  // If f maps an 3-dimensional vector x to an 3-dimensional vector y = f(x), J is
   276  // a 3×3 matrix whose elements are given as
   277  //
   278  //	J_{i,j} = ∂f_i/∂x_j,
   279  //
   280  // or expanded out
   281  //
   282  //	    [ ∂f_1/∂x_1 ∂f_1/∂x_2 ∂f_1/∂x_3 ]
   283  //	J = [ ∂f_2/∂x_1 ∂f_2/∂x_2 ∂f_2/∂x_3 ]
   284  //	    [ ∂f_3/∂x_1 ∂f_3/∂x_2 ∂f_3/∂x_3 ]
   285  //
   286  // Jacobian expects the field's first order partial derivatives are all
   287  // continuous for correct results.
   288  func (m *Mat) Jacobian(p, step Vec, field func(Vec) Vec) {
   289  	dx := Vec{X: step.X}
   290  	dy := Vec{Y: step.Y}
   291  	dz := Vec{Z: step.Z}
   292  
   293  	dfdx := Scale(0.5/step.X, Sub(field(Add(p, dx)), field(Sub(p, dx))))
   294  	dfdy := Scale(0.5/step.Y, Sub(field(Add(p, dy)), field(Sub(p, dy))))
   295  	dfdz := Scale(0.5/step.Z, Sub(field(Add(p, dz)), field(Sub(p, dz))))
   296  	m.Set(0, 0, dfdx.X)
   297  	m.Set(0, 1, dfdy.X)
   298  	m.Set(0, 2, dfdz.X)
   299  
   300  	m.Set(1, 0, dfdx.Y)
   301  	m.Set(1, 1, dfdy.Y)
   302  	m.Set(1, 2, dfdz.Y)
   303  
   304  	m.Set(2, 0, dfdx.Z)
   305  	m.Set(2, 1, dfdy.Z)
   306  	m.Set(2, 2, dfdz.Z)
   307  }