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  }