gonum.org/v1/gonum@v0.14.0/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 // 37 // PowReal(NaN+xϵ₁+yϵ₂, ±0) = 1+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for any x and y 38 // PowReal(x, ±0) = 1 for any x 39 // PowReal(1+xϵ₁+yϵ₂, z) = 1+xzϵ₁+yzϵ₂+2xyzϵ₁ϵ₂ for any z 40 // PowReal(NaN+xϵ₁+yϵ₂, 1) = NaN+xϵ₁+yϵ₂+NaNϵ₁ϵ₂ for any x 41 // PowReal(x, 1) = x for any x 42 // PowReal(NaN+xϵ₁+xϵ₂, y) = NaN+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ 43 // PowReal(x, NaN) = NaN+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ 44 // PowReal(±0, y) = ±Inf for y an odd integer < 0 45 // PowReal(±0, -Inf) = +Inf 46 // PowReal(±0, +Inf) = +0 47 // PowReal(±0, y) = +Inf for finite y < 0 and not an odd integer 48 // PowReal(±0, y) = ±0 for y an odd integer > 0 49 // PowReal(±0, y) = +0 for finite y > 0 and not an odd integer 50 // PowReal(-1, ±Inf) = 1 51 // PowReal(x+0ϵ₁+0ϵ₂, +Inf) = +Inf+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for |x| > 1 52 // PowReal(x+xϵ₁+yϵ₂, +Inf) = +Inf+Infϵ₁+Infϵ₂+NaNϵ₁ϵ₂ for |x| > 1 53 // PowReal(x, -Inf) = +0+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for |x| > 1 54 // PowReal(x+yϵ₁+zϵ₂, +Inf) = +0+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for |x| < 1 55 // PowReal(x+0ϵ₁+0ϵ₂, -Inf) = +Inf+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for |x| < 1 56 // PowReal(x, -Inf) = +Inf-Infϵ₁-Infϵ₂+NaNϵ₁ϵ₂ for |x| < 1 57 // PowReal(+Inf, y) = +Inf for y > 0 58 // PowReal(+Inf, y) = +0 for y < 0 59 // PowReal(-Inf, y) = Pow(-0, -y) 60 // PowReal(x, y) = NaN+NaNϵ₁+NaNϵ₂+NaNϵ₁ϵ₂ for finite x < 0 and finite non-integer y 61 func PowReal(d Number, p float64) Number { 62 const tol = 1e-15 63 64 r := d.Real 65 if math.Abs(r) < tol { 66 if r >= 0 { 67 r = tol 68 } 69 if r < 0 { 70 r = -tol 71 } 72 } 73 deriv := p * math.Pow(r, p-1) 74 return Number{ 75 Real: math.Pow(d.Real, p), 76 E1mag: d.E1mag * deriv, 77 E2mag: d.E2mag * deriv, 78 E1E2mag: d.E1E2mag*deriv + p*(p-1)*d.E1mag*d.E2mag*math.Pow(r, (p-2)), 79 } 80 } 81 82 // Pow returns x**p, the base-x exponential of p. 83 func Pow(d, p Number) Number { 84 return Exp(Mul(p, Log(d))) 85 } 86 87 // Sqrt returns the square root of d. 88 // 89 // Special cases are: 90 // 91 // Sqrt(+Inf) = +Inf 92 // Sqrt(±0) = (±0+Infϵ₁+Infϵ₂-Infϵ₁ϵ₂) 93 // Sqrt(x < 0) = NaN 94 // Sqrt(NaN) = NaN 95 func Sqrt(d Number) Number { 96 if d.Real <= 0 { 97 if d.Real == 0 { 98 return Number{ 99 Real: d.Real, 100 E1mag: math.Inf(1), 101 E2mag: math.Inf(1), 102 E1E2mag: math.Inf(-1), 103 } 104 } 105 return Number{ 106 Real: math.NaN(), 107 E1mag: math.NaN(), 108 E2mag: math.NaN(), 109 E1E2mag: math.NaN(), 110 } 111 } 112 return PowReal(d, 0.5) 113 } 114 115 // Exp returns e**q, the base-e exponential of d. 116 // 117 // Special cases are: 118 // 119 // Exp(+Inf) = +Inf 120 // Exp(NaN) = NaN 121 // 122 // Very large values overflow to 0 or +Inf. 123 // Very small values underflow to 1. 124 func Exp(d Number) Number { 125 exp := math.Exp(d.Real) // exp is also the derivative. 126 return Number{ 127 Real: exp, 128 E1mag: exp * d.E1mag, 129 E2mag: exp * d.E2mag, 130 E1E2mag: exp * (d.E1E2mag + d.E1mag*d.E2mag), 131 } 132 } 133 134 // Log returns the natural logarithm of d. 135 // 136 // Special cases are: 137 // 138 // Log(+Inf) = (+Inf+0ϵ₁+0ϵ₂-0ϵ₁ϵ₂) 139 // Log(0) = (-Inf±Infϵ₁±Infϵ₂-Infϵ₁ϵ₂) 140 // Log(x < 0) = NaN 141 // Log(NaN) = NaN 142 func Log(d Number) Number { 143 switch d.Real { 144 case 0: 145 return Number{ 146 Real: math.Log(d.Real), 147 E1mag: math.Copysign(math.Inf(1), d.Real), 148 E2mag: math.Copysign(math.Inf(1), d.Real), 149 E1E2mag: math.Inf(-1), 150 } 151 case math.Inf(1): 152 return Number{ 153 Real: math.Log(d.Real), 154 E1mag: 0, 155 E2mag: 0, 156 E1E2mag: negZero, 157 } 158 } 159 if d.Real < 0 { 160 return Number{ 161 Real: math.NaN(), 162 E1mag: math.NaN(), 163 E2mag: math.NaN(), 164 E1E2mag: math.NaN(), 165 } 166 } 167 deriv1 := d.E1mag / d.Real 168 deriv2 := d.E2mag / d.Real 169 return Number{ 170 Real: math.Log(d.Real), 171 E1mag: deriv1, 172 E2mag: deriv2, 173 E1E2mag: d.E1E2mag/d.Real - (deriv1 * deriv2), 174 } 175 } 176 177 // Sin returns the sine of d. 178 // 179 // Special cases are: 180 // 181 // Sin(±0) = (±0+Nϵ₁+Nϵ₂∓0ϵ₁ϵ₂) 182 // Sin(±Inf) = NaN 183 // Sin(NaN) = NaN 184 func Sin(d Number) Number { 185 if d.Real == 0 { 186 return Number{ 187 Real: d.Real, 188 E1mag: d.E1mag, 189 E2mag: d.E2mag, 190 E1E2mag: -d.Real, 191 } 192 } 193 fn := math.Sin(d.Real) 194 deriv := math.Cos(d.Real) 195 return Number{ 196 Real: fn, 197 E1mag: deriv * d.E1mag, 198 E2mag: deriv * d.E2mag, 199 E1E2mag: deriv*d.E1E2mag - fn*d.E1mag*d.E2mag, 200 } 201 } 202 203 // Cos returns the cosine of d. 204 // 205 // Special cases are: 206 // 207 // Cos(±Inf) = NaN 208 // Cos(NaN) = NaN 209 func Cos(d Number) Number { 210 fn := math.Cos(d.Real) 211 deriv := -math.Sin(d.Real) 212 return Number{ 213 Real: fn, 214 E1mag: deriv * d.E1mag, 215 E2mag: deriv * d.E2mag, 216 E1E2mag: deriv*d.E1E2mag - fn*d.E1mag*d.E2mag, 217 } 218 } 219 220 // Tan returns the tangent of d. 221 // 222 // Special cases are: 223 // 224 // Tan(±0) = (±0+Nϵ₁+Nϵ₂±0ϵ₁ϵ₂) 225 // Tan(±Inf) = NaN 226 // Tan(NaN) = NaN 227 func Tan(d Number) Number { 228 if d.Real == 0 { 229 return Number{ 230 Real: d.Real, 231 E1mag: d.E1mag, 232 E2mag: d.E2mag, 233 E1E2mag: d.Real, 234 } 235 } 236 fn := math.Tan(d.Real) 237 deriv := 1 + fn*fn 238 return Number{ 239 Real: fn, 240 E1mag: deriv * d.E1mag, 241 E2mag: deriv * d.E2mag, 242 E1E2mag: deriv*d.E1E2mag + d.E1mag*d.E2mag*(2*fn*deriv), 243 } 244 } 245 246 // Asin returns the inverse sine of d. 247 // 248 // Special cases are: 249 // 250 // Asin(±0) = (±0+Nϵ₁+Nϵ₂±0ϵ₁ϵ₂) 251 // Asin(±1) = (±Inf+Infϵ₁+Infϵ₂±Infϵ₁ϵ₂) 252 // Asin(x) = NaN if x < -1 or x > 1 253 func Asin(d Number) Number { 254 if d.Real == 0 { 255 return Number{ 256 Real: d.Real, 257 E1mag: d.E1mag, 258 E2mag: d.E2mag, 259 E1E2mag: d.Real, 260 } 261 } else if m := math.Abs(d.Real); m >= 1 { 262 if m == 1 { 263 return Number{ 264 Real: math.Asin(d.Real), 265 E1mag: math.Inf(1), 266 E2mag: math.Inf(1), 267 E1E2mag: math.Copysign(math.Inf(1), d.Real), 268 } 269 } 270 return Number{ 271 Real: math.NaN(), 272 E1mag: math.NaN(), 273 E2mag: math.NaN(), 274 E1E2mag: math.NaN(), 275 } 276 } 277 fn := math.Asin(d.Real) 278 deriv1 := 1 - d.Real*d.Real 279 deriv := 1 / math.Sqrt(deriv1) 280 return Number{ 281 Real: fn, 282 E1mag: deriv * d.E1mag, 283 E2mag: deriv * d.E2mag, 284 E1E2mag: deriv*d.E1E2mag + d.E1mag*d.E2mag*(d.Real*math.Pow(deriv1, -1.5)), 285 } 286 } 287 288 // Acos returns the inverse cosine of d. 289 // 290 // Special cases are: 291 // 292 // Acos(-1) = (Pi-Infϵ₁-Infϵ₂+Infϵ₁ϵ₂) 293 // Acos(1) = (0-Infϵ₁-Infϵ₂-Infϵ₁ϵ₂) 294 // Acos(x) = NaN if x < -1 or x > 1 295 func Acos(d Number) Number { 296 if m := math.Abs(d.Real); m >= 1 { 297 if m == 1 { 298 return Number{ 299 Real: math.Acos(d.Real), 300 E1mag: math.Inf(-1), 301 E2mag: math.Inf(-1), 302 E1E2mag: math.Copysign(math.Inf(1), -d.Real), 303 } 304 } 305 return Number{ 306 Real: math.NaN(), 307 E1mag: math.NaN(), 308 E2mag: math.NaN(), 309 E1E2mag: math.NaN(), 310 } 311 } 312 fn := math.Acos(d.Real) 313 deriv1 := 1 - d.Real*d.Real 314 deriv := -1 / math.Sqrt(deriv1) 315 return Number{ 316 Real: fn, 317 E1mag: deriv * d.E1mag, 318 E2mag: deriv * d.E2mag, 319 E1E2mag: deriv*d.E1E2mag + d.E1mag*d.E2mag*(-d.Real*math.Pow(deriv1, -1.5)), 320 } 321 } 322 323 // Atan returns the inverse tangent of d. 324 // 325 // Special cases are: 326 // 327 // Atan(±0) = (±0+Nϵ₁+Nϵ₂∓0ϵ₁ϵ₂) 328 // Atan(±Inf) = (±Pi/2+0ϵ₁+0ϵ₂∓0ϵ₁ϵ₂) 329 func Atan(d Number) Number { 330 if d.Real == 0 { 331 return Number{ 332 Real: d.Real, 333 E1mag: d.E1mag, 334 E2mag: d.E2mag, 335 E1E2mag: -d.Real, 336 } 337 } 338 fn := math.Atan(d.Real) 339 deriv1 := 1 + d.Real*d.Real 340 deriv := 1 / deriv1 341 return Number{ 342 Real: fn, 343 E1mag: deriv * d.E1mag, 344 E2mag: deriv * d.E2mag, 345 E1E2mag: deriv*d.E1E2mag + d.E1mag*d.E2mag*(-2*d.Real/(deriv1*deriv1)), 346 } 347 }