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 }