gonum.org/v1/gonum@v0.14.0/num/dualcmplx/dual.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  package dualcmplx
     6  
     7  import (
     8  	"fmt"
     9  	"math"
    10  	"math/cmplx"
    11  	"strings"
    12  )
    13  
    14  // Number is a float64 precision anti-commutative dual complex number.
    15  type Number struct {
    16  	Real, Dual complex128
    17  }
    18  
    19  // Format implements fmt.Formatter.
    20  func (d Number) Format(fs fmt.State, c rune) {
    21  	prec, pOk := fs.Precision()
    22  	if !pOk {
    23  		prec = -1
    24  	}
    25  	width, wOk := fs.Width()
    26  	if !wOk {
    27  		width = -1
    28  	}
    29  	switch c {
    30  	case 'v':
    31  		if fs.Flag('#') {
    32  			fmt.Fprintf(fs, "%T{Real:%#v, Dual:%#v}", d, d.Real, d.Dual)
    33  			return
    34  		}
    35  		if fs.Flag('+') {
    36  			fmt.Fprintf(fs, "{Real:%+v, Dual:%+v}", d.Real, d.Dual)
    37  			return
    38  		}
    39  		c = 'g'
    40  		prec = -1
    41  		fallthrough
    42  	case 'e', 'E', 'f', 'F', 'g', 'G':
    43  		fre := fmtString(fs, c, prec, width, false)
    44  		fim := fmtString(fs, c, prec, width, true)
    45  		fmt.Fprintf(fs, fmt.Sprintf("(%s+%[2]sϵ)", fre, fim), d.Real, d.Dual)
    46  	default:
    47  		fmt.Fprintf(fs, "%%!%c(%T=%[2]v)", c, d)
    48  		return
    49  	}
    50  }
    51  
    52  // This is horrible, but it's what we have.
    53  func fmtString(fs fmt.State, c rune, prec, width int, wantPlus bool) string {
    54  	var b strings.Builder
    55  	b.WriteByte('%')
    56  	for _, f := range "0+- " {
    57  		if fs.Flag(int(f)) || (f == '+' && wantPlus) {
    58  			b.WriteByte(byte(f))
    59  		}
    60  	}
    61  	if width >= 0 {
    62  		fmt.Fprint(&b, width)
    63  	}
    64  	if prec >= 0 {
    65  		b.WriteByte('.')
    66  		if prec > 0 {
    67  			fmt.Fprint(&b, prec)
    68  		}
    69  	}
    70  	b.WriteRune(c)
    71  	return b.String()
    72  }
    73  
    74  // Add returns the sum of x and y.
    75  func Add(x, y Number) Number {
    76  	return Number{
    77  		Real: x.Real + y.Real,
    78  		Dual: x.Dual + y.Dual,
    79  	}
    80  }
    81  
    82  // Sub returns the difference of x and y, x-y.
    83  func Sub(x, y Number) Number {
    84  	return Number{
    85  		Real: x.Real - y.Real,
    86  		Dual: x.Dual - y.Dual,
    87  	}
    88  }
    89  
    90  // Mul returns the dual product of x and y, x×y.
    91  func Mul(x, y Number) Number {
    92  	return Number{
    93  		Real: x.Real * y.Real,
    94  		Dual: x.Real*y.Dual + x.Dual*cmplx.Conj(y.Real),
    95  	}
    96  }
    97  
    98  // Inv returns the dual inverse of d.
    99  func Inv(d Number) Number {
   100  	return Number{
   101  		Real: 1 / d.Real,
   102  		Dual: -d.Dual / (d.Real * cmplx.Conj(d.Real)),
   103  	}
   104  }
   105  
   106  // Conj returns the conjugate of d₁+d₂ϵ, d̅₁+d₂ϵ.
   107  func Conj(d Number) Number {
   108  	return Number{
   109  		Real: cmplx.Conj(d.Real),
   110  		Dual: d.Dual,
   111  	}
   112  }
   113  
   114  // Scale returns d scaled by f.
   115  func Scale(f float64, d Number) Number {
   116  	return Number{Real: complex(f, 0) * d.Real, Dual: complex(f, 0) * d.Dual}
   117  }
   118  
   119  // Abs returns the absolute value of d.
   120  func Abs(d Number) float64 {
   121  	return cmplx.Abs(d.Real)
   122  }
   123  
   124  // PowReal returns d**p, the base-d exponential of p.
   125  //
   126  // Special cases are (in order):
   127  //
   128  //	PowReal(NaN+xϵ, ±0) = 1+NaNϵ for any x
   129  //	Pow(0+xϵ, y) = 0+Infϵ for all y < 1.
   130  //	Pow(0+xϵ, y) = 0 for all y > 1.
   131  //	PowReal(x, ±0) = 1 for any x
   132  //	PowReal(1+xϵ, y) = 1+xyϵ for any y
   133  //	Pow(Inf, y) = +Inf+NaNϵ for y > 0
   134  //	Pow(Inf, y) = +0+NaNϵ for y < 0
   135  //	PowReal(x, 1) = x for any x
   136  //	PowReal(NaN+xϵ, y) = NaN+NaNϵ
   137  //	PowReal(x, NaN) = NaN+NaNϵ
   138  //	PowReal(-1, ±Inf) = 1
   139  //	PowReal(x+0ϵ, +Inf) = +Inf+NaNϵ for |x| > 1
   140  //	PowReal(x+yϵ, +Inf) = +Inf for |x| > 1
   141  //	PowReal(x, -Inf) = +0+NaNϵ for |x| > 1
   142  //	PowReal(x, +Inf) = +0+NaNϵ for |x| < 1
   143  //	PowReal(x+0ϵ, -Inf) = +Inf+NaNϵ for |x| < 1
   144  //	PowReal(x, -Inf) = +Inf-Infϵ for |x| < 1
   145  //	PowReal(+Inf, y) = +Inf for y > 0
   146  //	PowReal(+Inf, y) = +0 for y < 0
   147  //	PowReal(-Inf, y) = Pow(-0, -y)
   148  func PowReal(d Number, p float64) Number {
   149  	switch {
   150  	case p == 0:
   151  		switch {
   152  		case cmplx.IsNaN(d.Real):
   153  			return Number{Real: 1, Dual: cmplx.NaN()}
   154  		case d.Real == 0, cmplx.IsInf(d.Real):
   155  			return Number{Real: 1}
   156  		}
   157  	case p == 1:
   158  		if cmplx.IsInf(d.Real) {
   159  			d.Dual = cmplx.NaN()
   160  		}
   161  		return d
   162  	case math.IsInf(p, 1):
   163  		if d.Real == -1 {
   164  			return Number{Real: 1, Dual: cmplx.NaN()}
   165  		}
   166  		if Abs(d) > 1 {
   167  			if d.Dual == 0 {
   168  				return Number{Real: cmplx.Inf(), Dual: cmplx.NaN()}
   169  			}
   170  			return Number{Real: cmplx.Inf(), Dual: cmplx.Inf()}
   171  		}
   172  		return Number{Real: 0, Dual: cmplx.NaN()}
   173  	case math.IsInf(p, -1):
   174  		if d.Real == -1 {
   175  			return Number{Real: 1, Dual: cmplx.NaN()}
   176  		}
   177  		if Abs(d) > 1 {
   178  			return Number{Real: 0, Dual: cmplx.NaN()}
   179  		}
   180  		if d.Dual == 0 {
   181  			return Number{Real: cmplx.Inf(), Dual: cmplx.NaN()}
   182  		}
   183  		return Number{Real: cmplx.Inf(), Dual: cmplx.Inf()}
   184  	case math.IsNaN(p):
   185  		return Number{Real: cmplx.NaN(), Dual: cmplx.NaN()}
   186  	case d.Real == 0:
   187  		if p < 1 {
   188  			return Number{Real: d.Real, Dual: cmplx.Inf()}
   189  		}
   190  		return Number{Real: d.Real}
   191  	case cmplx.IsInf(d.Real):
   192  		if p < 0 {
   193  			return Number{Real: 0, Dual: cmplx.NaN()}
   194  		}
   195  		return Number{Real: cmplx.Inf(), Dual: cmplx.NaN()}
   196  	}
   197  	return Pow(d, Number{Real: complex(p, 0)})
   198  }
   199  
   200  // Pow returns d**p, the base-d exponential of p.
   201  func Pow(d, p Number) Number {
   202  	return Exp(Mul(p, Log(d)))
   203  }
   204  
   205  // Sqrt returns the square root of d.
   206  //
   207  // Special cases are:
   208  //
   209  //	Sqrt(+Inf) = +Inf
   210  //	Sqrt(±0) = (±0+Infϵ)
   211  //	Sqrt(x < 0) = NaN
   212  //	Sqrt(NaN) = NaN
   213  func Sqrt(d Number) Number {
   214  	return PowReal(d, 0.5)
   215  }
   216  
   217  // Exp returns e**q, the base-e exponential of d.
   218  //
   219  // Special cases are:
   220  //
   221  //	Exp(+Inf) = +Inf
   222  //	Exp(NaN) = NaN
   223  //
   224  // Very large values overflow to 0 or +Inf.
   225  // Very small values underflow to 1.
   226  func Exp(d Number) Number {
   227  	fn := cmplx.Exp(d.Real)
   228  	if imag(d.Real) == 0 {
   229  		return Number{Real: fn, Dual: fn * d.Dual}
   230  	}
   231  	conj := cmplx.Conj(d.Real)
   232  	return Number{
   233  		Real: fn,
   234  		Dual: ((fn - cmplx.Exp(conj)) / (d.Real - conj)) * d.Dual,
   235  	}
   236  }
   237  
   238  // Log returns the natural logarithm of d.
   239  //
   240  // Special cases are:
   241  //
   242  //	Log(+Inf) = (+Inf+0ϵ)
   243  //	Log(0) = (-Inf±Infϵ)
   244  //	Log(x < 0) = NaN
   245  //	Log(NaN) = NaN
   246  func Log(d Number) Number {
   247  	fn := cmplx.Log(d.Real)
   248  	switch {
   249  	case d.Real == 0:
   250  		return Number{
   251  			Real: fn,
   252  			Dual: complex(math.Copysign(math.Inf(1), real(d.Real)), math.NaN()),
   253  		}
   254  	case imag(d.Real) == 0:
   255  		return Number{
   256  			Real: fn,
   257  			Dual: d.Dual / d.Real,
   258  		}
   259  	case cmplx.IsInf(d.Real):
   260  		return Number{
   261  			Real: fn,
   262  			Dual: 0,
   263  		}
   264  	}
   265  	conj := cmplx.Conj(d.Real)
   266  	return Number{
   267  		Real: fn,
   268  		Dual: ((fn - cmplx.Log(conj)) / (d.Real - conj)) * d.Dual,
   269  	}
   270  }