github.com/rmera/gochem@v0.7.1/v3/gocoords.go (about)

     1  /*
     2   * gocoords.go, part of gochem.
     3   *
     4   * Copyright 2012 Raul Mera <rmera{at}chemDOThelsinkiDOTfi>
     5   *
     6   * This program is free software; you can redistribute it and/or modify
     7   * it under the terms of the GNU Lesser General Public License as
     8   * published by the Free Software Foundation; either version 2.1 of the
     9   * License, or (at your option) any later version.
    10   *
    11   * This program is distributed in the hope that it will be useful,
    12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14   * GNU General Public License for more details.
    15   *
    16   * You should have received a copy of the GNU Lesser General
    17   * Public License along with this program.  If not, see
    18   * <http://www.gnu.org/licenses/>.
    19   *
    20   * Gochem is developed at the laboratory for instruction in Swedish, Department of Chemistry,
    21   * University of Helsinki, Finland.
    22   *
    23   */
    24  
    25  //Package chem provides atom and molecule structures, facilities for reading and writing some
    26  //files used in computational chemistry and some functions for geometric manipulations and shape
    27  //indicators.
    28  
    29  package v3
    30  
    31  import (
    32  	"fmt"
    33  	"math"
    34  	"strings"
    35  
    36  	"gonum.org/v1/gonum/mat"
    37  )
    38  
    39  const appzero float64 = 0.000000000001 //used to correct floating point
    40  //errors. Everything equal or less than this is considered zero. This probably sucks.
    41  
    42  //Zeros returns a zero-filled Matrix with vecs vectors and 3 in the other dimension.
    43  func Zeros(vecs int) *Matrix {
    44  	const cols int = 3
    45  	f := make([]float64, cols*vecs, cols*vecs)
    46  	return &Matrix{mat.NewDense(vecs, cols, f)}
    47  }
    48  
    49  //METHODS
    50  
    51  //SwapVecs swaps the vectors i and j in the receiver
    52  func (F *Matrix) SwapVecs(i, j int) {
    53  	if i >= F.NVecs() || j >= F.NVecs() {
    54  		panic(ErrIndexOutOfRange)
    55  	}
    56  	rowi := F.Row(nil, i)
    57  	rowj := F.Row(nil, j)
    58  	for k := 0; k < 3; k++ {
    59  		F.Set(i, k, rowj[k])
    60  		F.Set(j, k, rowi[k])
    61  	}
    62  }
    63  
    64  //AddVec adds a vector to the  coordmatrix A putting the result on the received.
    65  //depending on whether the underlying matrix to coordmatrix
    66  //is col or row major, it could add a col or a row vector.
    67  func (F *Matrix) AddVec(A, vec *Matrix) {
    68  	ar, ac := A.Dims()
    69  	rr, rc := vec.Dims()
    70  	fr, fc := F.Dims()
    71  	if ac != rc || rr != 1 || ac != fc || ar != fr {
    72  		panic(ErrShape)
    73  	}
    74  	var B *Matrix
    75  	if A.Dense == F.Dense { //Using identical matrices for this should be A-OK, but something changed in gonum. I am not sure of why is it forbidden now.
    76  		B = Zeros(A.NVecs())
    77  		B.Copy(A)
    78  	} else {
    79  		B = A
    80  	}
    81  	for i := 0; i < ar; i++ {
    82  		j := B.VecView(i)
    83  		f := F.VecView(i)
    84  		f.Dense.Add(j.Dense, vec.Dense)
    85  	}
    86  }
    87  
    88  //DelRow deletes a row in matrix A, placing the results
    89  //in the receiver. Equivalent to DelVec for compatibility.
    90  func (F *Matrix) DelRow(A *Matrix, i int) {
    91  	F.DelVec(A, i)
    92  }
    93  
    94  //DelVec deletes a (row) vector in matrix A, placing the results
    95  //in the receiver.
    96  func (F *Matrix) DelVec(A *Matrix, i int) {
    97  	ar, ac := A.Dims()
    98  	fr, fc := F.Dims()
    99  	if i >= ar || fc != ac || fr != (ar-1) {
   100  		panic(ErrShape)
   101  	}
   102  	tempA1 := A.View(0, 0, i, ac)
   103  	tempF1 := F.View(0, 0, i, ac)
   104  	tempF1.Copy(tempA1)
   105  	//now the other part
   106  	//	if i != ar-1 {
   107  	//fmt.Println("options", ar, i, ar-i-1)
   108  	if i < ar-1 {
   109  		tempA2 := A.View(i+1, 0, ar-i-1, ac) //The magic happens here
   110  		tempF2 := F.View(i, 0, ar-i-1, fc)
   111  		tempF2.Copy(tempA2)
   112  	}
   113  }
   114  
   115  //NVecs return the number of (row) vectors in F.
   116  func (F *Matrix) NVecs() int {
   117  	r, c := F.Dims()
   118  	if c != 3 {
   119  		panic(ErrNotXx3Matrix)
   120  	}
   121  	return r
   122  
   123  }
   124  
   125  //Len return the number of (row) vectors in F.
   126  //Equivalent to NVecs, but more in line with Go APIS.
   127  func (F *Matrix) Len() int {
   128  	return F.NVecs()
   129  }
   130  
   131  //ScaleByRow scales each coordinates in the  A by the coordinate in the row-vector coord.
   132  //The result is put in F. This is the old name fo the function, now called ScaleByVec. It is kept for compatibility
   133  func (F *Matrix) ScaleByRow(A, coord *Matrix) {
   134  	F.ScaleByVec(A, coord)
   135  }
   136  
   137  //SetVecs sets the vector F[clist[i]] to the vector A[i], for all indexes i in clist.
   138  //nth vector of A. Indexes i must be positive or 0
   139  func (F *Matrix) SetVecs(A *Matrix, clist []int) {
   140  	ar, ac := A.Dims()
   141  	fr, fc := F.Dims()
   142  	if ac != fc || fr < len(clist) || ar < len(clist) {
   143  		panic(ErrShape)
   144  	}
   145  	for key, val := range clist {
   146  		for j := 0; j < ac; j++ {
   147  			F.Set(val, j, A.At(key, j)) //This will panic if an index is less than zero, which is fine.
   148  		}
   149  	}
   150  }
   151  
   152  //SomeVecs Returns a matrix contaning a copy of the ith rows of matrix A,
   153  //where i are the numbers in clist. The rows are in the same order
   154  //than the clist. The numbers in clist must be positive or zero.
   155  func (F *Matrix) SomeVecs(A *Matrix, clist []int) {
   156  	ar, ac := A.Dims()
   157  	fr, fc := F.Dims()
   158  	if ac != fc || fr != len(clist) || ar < len(clist) {
   159  		panic(ErrShape)
   160  	}
   161  	for key, val := range clist {
   162  		for j := 0; j < ac; j++ {
   163  			F.Set(key, j, A.At(val, j))
   164  		}
   165  	}
   166  }
   167  
   168  //SomeVecsSafe returns a matrix contaning all the ith vectors of matrix A,
   169  //where i are the numbers in clist. The vectors are in the same order
   170  //than the clist. It will try to recover so it returns an error instead of panicking.
   171  func (F *Matrix) SomeVecsSafe(A *Matrix, clist []int) error {
   172  	var err error
   173  	defer func() {
   174  		if r := recover(); r != nil {
   175  			switch e := r.(type) {
   176  			case PanicMsg:
   177  				err = Error{fmt.Sprintf("%s: %s", ErrGonum, e), []string{"SomeVecsSafe"}, true}
   178  			case mat.Error:
   179  				err = Error{fmt.Sprintf("%%goChem/v3: gonum/matrix.Error: %s", e), []string{"SomeVecsSafe"}, true}
   180  			default:
   181  				panic(r)
   182  			}
   183  		}
   184  	}()
   185  	F.SomeVecs(A, clist)
   186  	return err
   187  }
   188  
   189  //StackVec puts in F a matrix consistent of A over B or A to the left of B.
   190  func (F *Matrix) StackVec(A, B *Matrix) {
   191  	F.Stack(A, B)
   192  }
   193  
   194  //String returns a neat string representation of a Matrix
   195  func (F *Matrix) String() string {
   196  	r, c := F.Dims()
   197  	v := make([]string, r+2, r+2)
   198  	v[0] = "\n["
   199  	v[len(v)-1] = " ]"
   200  	row := make([]float64, c, c)
   201  	for i := 0; i < r; i++ {
   202  		F.Row(row, i) //now row has a slice witht he row i
   203  		if i == 0 {
   204  			v[i+1] = fmt.Sprintf("%6.2f %6.2f %6.2f\n", row[0], row[1], row[2])
   205  			continue
   206  		} else if i == r-1 {
   207  			v[i+1] = fmt.Sprintf(" %6.2f %6.2f %6.2f", row[0], row[1], row[2])
   208  			continue
   209  		} else {
   210  			v[i+1] = fmt.Sprintf(" %6.2f %6.2f %6.2f\n", row[0], row[1], row[2])
   211  		}
   212  	}
   213  	v[len(v)-2] = strings.Replace(v[len(v)-2], "\n", "", 1)
   214  	return strings.Join(v, "")
   215  }
   216  
   217  //SubVec subtracts the vector  to each vector of the matrix A, putting
   218  //the result on the receiver. Panics if matrices are mismatched.  It will not
   219  //work if A and row reference to the same Matrix.
   220  func (F *Matrix) SubVec(A, vec *Matrix) {
   221  	vec.Scale(-1, vec)
   222  	F.AddVec(A, vec)
   223  	vec.Scale(-1, vec)
   224  }
   225  
   226  //Cross puts the cross product of the first vecs of a and b in the first vec of F. Panics if error.
   227  func (F *Matrix) Cross(a, b *Matrix) {
   228  	if a.NVecs() < 1 || b.NVecs() < 1 || F.NVecs() < 1 {
   229  		panic(ErrNoCrossProduct)
   230  	}
   231  	//I ask for Matrix instead of Matrix, even though  I only need the At method.
   232  	//This is so I dont need to ensure that the rows are taken, and thus I dont need to break the
   233  	//API if the matrices become col-major.
   234  	F.Set(0, 0, a.At(0, 1)*b.At(0, 2)-a.At(0, 2)*b.At(0, 1))
   235  	F.Set(0, 1, a.At(0, 2)*b.At(0, 0)-a.At(0, 0)*b.At(0, 2))
   236  	F.Set(0, 2, a.At(0, 0)*b.At(0, 1)-a.At(0, 1)*b.At(0, 0))
   237  }
   238  
   239  //METHODS Not Vec specific.
   240  
   241  //AddFloat puts in the receiver a matrix which elements are
   242  //those of matrix A plus the float B.
   243  func (F *Matrix) AddFloat(A *Matrix, B float64) {
   244  	ar, ac := A.Dims()
   245  	if F != A {
   246  		F.Copy(A)
   247  	}
   248  	for i := 0; i < ar; i++ {
   249  		for j := 0; j < ac; j++ {
   250  			F.Set(i, j, A.At(i, j)+B)
   251  		}
   252  	}
   253  }
   254  
   255  //AddRow adds the row vector row to each row of the matrix A, putting
   256  //the result on the receiver. Panics if matrices are mismatched. It will not work if A and row
   257  //reference to the same Matrix.
   258  func (F *Matrix) AddRow(A, row *Matrix) {
   259  	F.AddVec(A, row)
   260  }
   261  
   262  //ScaleByCol scales each column of matrix A by Col, putting the result
   263  //in the received.
   264  func (F *Matrix) ScaleByCol(A, Col mat.Matrix) {
   265  	ar, ac := A.Dims()
   266  	cr, cc := Col.Dims()
   267  	fr, fc := F.Dims()
   268  	if ar != cr || cc > 1 || ar != fr || ac != fc {
   269  		panic(ErrShape)
   270  	}
   271  	if F != A {
   272  		F.Copy(A)
   273  	}
   274  	for i := 0; i < ac; i++ {
   275  		temp := F.ColView(i)
   276  
   277  		temp.Dense.MulElem(temp.Dense, Col)
   278  	}
   279  
   280  }
   281  
   282  //ScaleByVec scales each column of matrix A by Vec, putting the result
   283  //in the received.
   284  func (F *Matrix) ScaleByVec(A, Vec *Matrix) {
   285  	ar, ac := A.Dims()
   286  	rr, rc := Vec.Dims()
   287  	fr, fc := F.Dims()
   288  	if ac != rc || rr != 1 || ar != fr || ac != fc {
   289  		panic(ErrShape)
   290  	}
   291  	//	if F != A {
   292  	//		F.Copy(A)
   293  	//	}
   294  	for i := 0; i < ac; i++ {
   295  		temp := F.RowView(i)
   296  		temp.Dense.MulElem(temp.Dense, Vec.Dense)
   297  	}
   298  }
   299  
   300  //RowView puts a view of the given row of the matrix in the receiver
   301  //Equivalent to VecView
   302  func (F *Matrix) RowView(i int) *Matrix {
   303  	return F.VecView(i)
   304  }
   305  
   306  //SubRow subtracts the row vector row to each row of the matrix A, putting
   307  //the result on the receiver. Panics if matrices are mismatched.  It will not
   308  //work if A and row reference to the same Matrix.
   309  func (F *Matrix) SubRow(A, row *Matrix) {
   310  	F.SubVec(A, row)
   311  }
   312  
   313  //Unit puts in the receiver the unit vector pointing in the same
   314  //direction as the vector A (A divided by its norm).
   315  func (F *Matrix) Unit(A *Matrix) {
   316  	if A.Dense != F.Dense {
   317  		F.Copy(A)
   318  	}
   319  	norm := 1.0 / F.Norm(2)
   320  	F.Scale(norm, F)
   321  }
   322  
   323  //KronekerDelta is a naive implementation of the kroneker delta function.
   324  func KronekerDelta(a, b, epsilon float64) float64 {
   325  	if epsilon < 0 {
   326  		epsilon = appzero
   327  	}
   328  	if math.Abs(a-b) <= epsilon {
   329  		return 1
   330  	}
   331  	return 0
   332  }