github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/num/hyperdual/hyperdual_fike.go (about) 1 // Copyright ©2018 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 // Derived from code by Jeffrey A. Fike at http://adl.stanford.edu/hyperdual/ 6 7 // The MIT License (MIT) 8 // 9 // Copyright (c) 2006 Jeffrey A. Fike 10 // 11 // Permission is hereby granted, free of charge, to any person obtaining a copy 12 // of this software and associated documentation files (the "Software"), to deal 13 // in the Software without restriction, including without limitation the rights 14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 // copies of the Software, and to permit persons to whom the Software is 16 // furnished to do so, subject to the following conditions: 17 // 18 // The above copyright notice and this permission notice shall be included in 19 // all copies or substantial portions of the Software. 20 // 21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 // THE SOFTWARE. 28 29 package hyperdual 30 31 import "math" 32 33 // PowReal returns x**p, the base-x exponential of p. 34 // 35 // Special cases are (in order): 36 // PowReal(NaN+xϵ₁+yϵ₂, ±0) = 1+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for any x and y 37 // PowReal(x, ±0) = 1 for any x 38 // PowReal(1+xϵ₁+yϵ₂, z) = 1+xzϵ₁+yzϵ₂+2xyzϵ₁ϵ₂ for any z 39 // PowReal(NaN+xϵ₁+yϵ₂, 1) = NaN+xϵ₁+yϵ₂+NaNϵ₁ϵ₂ for any x 40 // PowReal(x, 1) = x for any x 41 // PowReal(NaN+xϵ₁+xϵ₂, y) = NaN+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ 42 // PowReal(x, NaN) = NaN+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ 43 // PowReal(±0, y) = ±Inf for y an odd integer < 0 44 // PowReal(±0, -Inf) = +Inf 45 // PowReal(±0, +Inf) = +0 46 // PowReal(±0, y) = +Inf for finite y < 0 and not an odd integer 47 // PowReal(±0, y) = ±0 for y an odd integer > 0 48 // PowReal(±0, y) = +0 for finite y > 0 and not an odd integer 49 // PowReal(-1, ±Inf) = 1 50 // PowReal(x+0ϵ₁+0ϵ₂, +Inf) = +Inf+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for |x| > 1 51 // PowReal(x+xϵ₁+yϵ₂, +Inf) = +Inf+Infϵ₁+Infϵ₂+NaNϵ₁ϵ₂ for |x| > 1 52 // PowReal(x, -Inf) = +0+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for |x| > 1 53 // PowReal(x+yϵ₁+zϵ₂, +Inf) = +0+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for |x| < 1 54 // PowReal(x+0ϵ₁+0ϵ₂, -Inf) = +Inf+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for |x| < 1 55 // PowReal(x, -Inf) = +Inf-Infϵ₁-Infϵ₂+NaNϵ₁ϵ₂ for |x| < 1 56 // PowReal(+Inf, y) = +Inf for y > 0 57 // PowReal(+Inf, y) = +0 for y < 0 58 // PowReal(-Inf, y) = Pow(-0, -y) 59 // PowReal(x, y) = NaN+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for finite x < 0 and finite non-integer y 60 func PowReal(d Number, p float64) Number { 61 const tol = 1e-15 62 63 r := d.Real 64 if math.Abs(r) < tol { 65 if r >= 0 { 66 r = tol 67 } 68 if r < 0 { 69 r = -tol 70 } 71 } 72 deriv := p * math.Pow(r, p-1) 73 return Number{ 74 Real: math.Pow(d.Real, p), 75 E1mag: d.E1mag * deriv, 76 E2mag: d.E2mag * deriv, 77 E1E2mag: d.E1E2mag*deriv + p*(p-1)*d.E1mag*d.E2mag*math.Pow(r, (p-2)), 78 } 79 } 80 81 // Pow returns x**p, the base-x exponential of p. 82 func Pow(d, p Number) Number { 83 return Exp(Mul(p, Log(d))) 84 } 85 86 // Sqrt returns the square root of d. 87 // 88 // Special cases are: 89 // Sqrt(+Inf) = +Inf 90 // Sqrt(±0) = (±0+Infϵ₁+Infϵ₂-Infϵ₁ϵ₂) 91 // Sqrt(x < 0) = NaN 92 // Sqrt(NaN) = NaN 93 func Sqrt(d Number) Number { 94 if d.Real <= 0 { 95 if d.Real == 0 { 96 return Number{ 97 Real: d.Real, 98 E1mag: math.Inf(1), 99 E2mag: math.Inf(1), 100 E1E2mag: math.Inf(-1), 101 } 102 } 103 return Number{ 104 Real: math.NaN(), 105 E1mag: math.NaN(), 106 E2mag: math.NaN(), 107 E1E2mag: math.NaN(), 108 } 109 } 110 return PowReal(d, 0.5) 111 } 112 113 // Exp returns e**q, the base-e exponential of d. 114 // 115 // Special cases are: 116 // Exp(+Inf) = +Inf 117 // Exp(NaN) = NaN 118 // Very large values overflow to 0 or +Inf. 119 // Very small values underflow to 1. 120 func Exp(d Number) Number { 121 exp := math.Exp(d.Real) // exp is also the derivative. 122 return Number{ 123 Real: exp, 124 E1mag: exp * d.E1mag, 125 E2mag: exp * d.E2mag, 126 E1E2mag: exp * (d.E1E2mag + d.E1mag*d.E2mag), 127 } 128 } 129 130 // Log returns the natural logarithm of d. 131 // 132 // Special cases are: 133 // Log(+Inf) = (+Inf+0ϵ₁+0ϵ₂-0ϵ₁ϵ₂) 134 // Log(0) = (-Inf±Infϵ₁±Infϵ₂-Infϵ₁ϵ₂) 135 // Log(x < 0) = NaN 136 // Log(NaN) = NaN 137 func Log(d Number) Number { 138 switch d.Real { 139 case 0: 140 return Number{ 141 Real: math.Log(d.Real), 142 E1mag: math.Copysign(math.Inf(1), d.Real), 143 E2mag: math.Copysign(math.Inf(1), d.Real), 144 E1E2mag: math.Inf(-1), 145 } 146 case math.Inf(1): 147 return Number{ 148 Real: math.Log(d.Real), 149 E1mag: 0, 150 E2mag: 0, 151 E1E2mag: negZero, 152 } 153 } 154 if d.Real < 0 { 155 return Number{ 156 Real: math.NaN(), 157 E1mag: math.NaN(), 158 E2mag: math.NaN(), 159 E1E2mag: math.NaN(), 160 } 161 } 162 deriv1 := d.E1mag / d.Real 163 deriv2 := d.E2mag / d.Real 164 return Number{ 165 Real: math.Log(d.Real), 166 E1mag: deriv1, 167 E2mag: deriv2, 168 E1E2mag: d.E1E2mag/d.Real - (deriv1 * deriv2), 169 } 170 } 171 172 // Sin returns the sine of d. 173 // 174 // Special cases are: 175 // Sin(±0) = (±0+Nϵ₁+Nϵ₂∓0ϵ₁ϵ₂) 176 // Sin(±Inf) = NaN 177 // Sin(NaN) = NaN 178 func Sin(d Number) Number { 179 if d.Real == 0 { 180 return Number{ 181 Real: d.Real, 182 E1mag: d.E1mag, 183 E2mag: d.E2mag, 184 E1E2mag: -d.Real, 185 } 186 } 187 fn := math.Sin(d.Real) 188 deriv := math.Cos(d.Real) 189 return Number{ 190 Real: fn, 191 E1mag: deriv * d.E1mag, 192 E2mag: deriv * d.E2mag, 193 E1E2mag: deriv*d.E1E2mag - fn*d.E1mag*d.E2mag, 194 } 195 } 196 197 // Cos returns the cosine of d. 198 // 199 // Special cases are: 200 // Cos(±Inf) = NaN 201 // Cos(NaN) = NaN 202 func Cos(d Number) Number { 203 fn := math.Cos(d.Real) 204 deriv := -math.Sin(d.Real) 205 return Number{ 206 Real: fn, 207 E1mag: deriv * d.E1mag, 208 E2mag: deriv * d.E2mag, 209 E1E2mag: deriv*d.E1E2mag - fn*d.E1mag*d.E2mag, 210 } 211 } 212 213 // Tan returns the tangent of d. 214 // 215 // Special cases are: 216 // Tan(±0) = (±0+Nϵ₁+Nϵ₂±0ϵ₁ϵ₂) 217 // Tan(±Inf) = NaN 218 // Tan(NaN) = NaN 219 func Tan(d Number) Number { 220 if d.Real == 0 { 221 return Number{ 222 Real: d.Real, 223 E1mag: d.E1mag, 224 E2mag: d.E2mag, 225 E1E2mag: d.Real, 226 } 227 } 228 fn := math.Tan(d.Real) 229 deriv := 1 + fn*fn 230 return Number{ 231 Real: fn, 232 E1mag: deriv * d.E1mag, 233 E2mag: deriv * d.E2mag, 234 E1E2mag: deriv*d.E1E2mag + d.E1mag*d.E2mag*(2*fn*deriv), 235 } 236 } 237 238 // Asin returns the inverse sine of d. 239 // 240 // Special cases are: 241 // Asin(±0) = (±0+Nϵ₁+Nϵ₂±0ϵ₁ϵ₂) 242 // Asin(±1) = (±Inf+Infϵ₁+Infϵ₂±Infϵ₁ϵ₂) 243 // Asin(x) = NaN if x < -1 or x > 1 244 func Asin(d Number) Number { 245 if d.Real == 0 { 246 return Number{ 247 Real: d.Real, 248 E1mag: d.E1mag, 249 E2mag: d.E2mag, 250 E1E2mag: d.Real, 251 } 252 } else if m := math.Abs(d.Real); m >= 1 { 253 if m == 1 { 254 return Number{ 255 Real: math.Asin(d.Real), 256 E1mag: math.Inf(1), 257 E2mag: math.Inf(1), 258 E1E2mag: math.Copysign(math.Inf(1), d.Real), 259 } 260 } 261 return Number{ 262 Real: math.NaN(), 263 E1mag: math.NaN(), 264 E2mag: math.NaN(), 265 E1E2mag: math.NaN(), 266 } 267 } 268 fn := math.Asin(d.Real) 269 deriv1 := 1 - d.Real*d.Real 270 deriv := 1 / math.Sqrt(deriv1) 271 return Number{ 272 Real: fn, 273 E1mag: deriv * d.E1mag, 274 E2mag: deriv * d.E2mag, 275 E1E2mag: deriv*d.E1E2mag + d.E1mag*d.E2mag*(d.Real*math.Pow(deriv1, -1.5)), 276 } 277 } 278 279 // Acos returns the inverse cosine of d. 280 // 281 // Special cases are: 282 // Acos(-1) = (Pi-Infϵ₁-Infϵ₂+Infϵ₁ϵ₂) 283 // Acos(1) = (0-Infϵ₁-Infϵ₂-Infϵ₁ϵ₂) 284 // Acos(x) = NaN if x < -1 or x > 1 285 func Acos(d Number) Number { 286 if m := math.Abs(d.Real); m >= 1 { 287 if m == 1 { 288 return Number{ 289 Real: math.Acos(d.Real), 290 E1mag: math.Inf(-1), 291 E2mag: math.Inf(-1), 292 E1E2mag: math.Copysign(math.Inf(1), -d.Real), 293 } 294 } 295 return Number{ 296 Real: math.NaN(), 297 E1mag: math.NaN(), 298 E2mag: math.NaN(), 299 E1E2mag: math.NaN(), 300 } 301 } 302 fn := math.Acos(d.Real) 303 deriv1 := 1 - d.Real*d.Real 304 deriv := -1 / math.Sqrt(deriv1) 305 return Number{ 306 Real: fn, 307 E1mag: deriv * d.E1mag, 308 E2mag: deriv * d.E2mag, 309 E1E2mag: deriv*d.E1E2mag + d.E1mag*d.E2mag*(-d.Real*math.Pow(deriv1, -1.5)), 310 } 311 } 312 313 // Atan returns the inverse tangent of d. 314 // 315 // Special cases are: 316 // Atan(±0) = (±0+Nϵ₁+Nϵ₂∓0ϵ₁ϵ₂) 317 // Atan(±Inf) = (±Pi/2+0ϵ₁+0ϵ₂∓0ϵ₁ϵ₂) 318 func Atan(d Number) Number { 319 if d.Real == 0 { 320 return Number{ 321 Real: d.Real, 322 E1mag: d.E1mag, 323 E2mag: d.E2mag, 324 E1E2mag: -d.Real, 325 } 326 } 327 fn := math.Atan(d.Real) 328 deriv1 := 1 + d.Real*d.Real 329 deriv := 1 / deriv1 330 return Number{ 331 Real: fn, 332 E1mag: deriv * d.E1mag, 333 E2mag: deriv * d.E2mag, 334 E1E2mag: deriv*d.E1E2mag + d.E1mag*d.E2mag*(-2*d.Real/(deriv1*deriv1)), 335 } 336 }