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  }