- +

Welcome to SymPy’s documentation!

SymPy is a Python library for symbolic mathematics. @@ -189,20 +232,14 @@

Welcome to SymPy’s documentation!

-

Docs for other versions

-

SymPy 0.6.7

-

SymPy 0.7.0

-

SymPy 0.7.1

-

SymPy 0.7.2

-

SymPy 0.7.2 (Python 3)

-

SymPy 0.7.3

-

SymPy git

-

SymPy 0.7.4

-

SymPy 0.7.4.1

-

SymPy 0.7.5

-

SymPy 0.7.6

-

SymPy 0.7.6.1

-

SymPy 1.0

+
+ +

Next topic

Installation

@@ -242,7 +279,7 @@

Navigation

  • next |
  • -
  • SymPy 0.7.7.dev documentation »
  • +
  • SymPy 0.7.7.dev documentation »
  • - \ No newline at end of file + diff --git a/latest/_images/ai.png b/latest/_images/ai.png new file mode 100644 index 000000000000..96762ce15a05 Binary files /dev/null and b/latest/_images/ai.png differ diff --git a/latest/_images/ai_c.png b/latest/_images/ai_c.png new file mode 100644 index 000000000000..5963ed086953 Binary files /dev/null and b/latest/_images/ai_c.png differ diff --git a/latest/_images/ber.png b/latest/_images/ber.png new file mode 100644 index 000000000000..2646482bd3b4 Binary files /dev/null and b/latest/_images/ber.png differ diff --git a/latest/_images/besseli.png b/latest/_images/besseli.png new file mode 100644 index 000000000000..1aae4b354db9 Binary files /dev/null and b/latest/_images/besseli.png differ diff --git a/latest/_images/besseli_c.png b/latest/_images/besseli_c.png new file mode 100644 index 000000000000..ee3f2d43a872 Binary files /dev/null and b/latest/_images/besseli_c.png differ diff --git a/latest/_images/besselj.png b/latest/_images/besselj.png new file mode 100644 index 000000000000..4bc13b379ab0 Binary files /dev/null and b/latest/_images/besselj.png differ diff --git a/latest/_images/besselj_c.png b/latest/_images/besselj_c.png new file mode 100644 index 000000000000..507634b31c3e Binary files /dev/null and b/latest/_images/besselj_c.png differ diff --git a/latest/_images/besselk.png b/latest/_images/besselk.png new file mode 100644 index 000000000000..19501d1fe0d4 Binary files /dev/null and b/latest/_images/besselk.png differ diff --git a/latest/_images/besselk_c.png b/latest/_images/besselk_c.png new file mode 100644 index 000000000000..e521824fa79d Binary files /dev/null and b/latest/_images/besselk_c.png differ diff --git a/latest/_images/bessely.png b/latest/_images/bessely.png new file mode 100644 index 000000000000..0766d7c50e77 Binary files /dev/null and b/latest/_images/bessely.png differ diff --git a/latest/_images/bessely_c.png b/latest/_images/bessely_c.png new file mode 100644 index 000000000000..e76be4737e88 Binary files /dev/null and b/latest/_images/bessely_c.png differ diff --git a/latest/_images/bi.png b/latest/_images/bi.png new file mode 100644 index 000000000000..b8800d7fdc48 Binary files /dev/null and b/latest/_images/bi.png differ diff --git a/latest/_images/bi_c.png b/latest/_images/bi_c.png new file mode 100644 index 000000000000..cbd17badae21 Binary files /dev/null and b/latest/_images/bi_c.png differ diff --git a/latest/_images/chebyt.png b/latest/_images/chebyt.png new file mode 100644 index 000000000000..ce78cecc2502 Binary files /dev/null and b/latest/_images/chebyt.png differ diff --git a/latest/_images/chebyu.png b/latest/_images/chebyu.png new file mode 100644 index 000000000000..591bb0e6fbf2 Binary files /dev/null and b/latest/_images/chebyu.png differ diff --git a/latest/_images/coulombf.png b/latest/_images/coulombf.png new file mode 100644 index 000000000000..a4a0efe33ad1 Binary files /dev/null and b/latest/_images/coulombf.png differ diff --git a/latest/_images/coulombf_c.png b/latest/_images/coulombf_c.png new file mode 100644 index 000000000000..bc34dffcd484 Binary files /dev/null and b/latest/_images/coulombf_c.png differ diff --git a/latest/_images/coulombg.png b/latest/_images/coulombg.png new file mode 100644 index 000000000000..bc6c02c0df22 Binary files /dev/null and b/latest/_images/coulombg.png differ diff --git a/latest/_images/coulombg_c.png b/latest/_images/coulombg_c.png new file mode 100644 index 000000000000..de6e7cdcfba3 Binary files /dev/null and b/latest/_images/coulombg_c.png differ diff --git a/latest/_images/cplot.png b/latest/_images/cplot.png new file mode 100644 index 000000000000..1b8c261875d4 Binary files /dev/null and b/latest/_images/cplot.png differ diff --git a/latest/_images/ellipe.png b/latest/_images/ellipe.png new file mode 100644 index 000000000000..d816a6b0fdb1 Binary files /dev/null and b/latest/_images/ellipe.png differ diff --git a/latest/_images/ellipf.png b/latest/_images/ellipf.png new file mode 100644 index 000000000000..30c5ec742eff Binary files /dev/null and b/latest/_images/ellipf.png differ diff --git a/latest/_images/ellipk.png b/latest/_images/ellipk.png new file mode 100644 index 000000000000..e44bbee2ac32 Binary files /dev/null and b/latest/_images/ellipk.png differ diff --git a/latest/_images/ellippi.png b/latest/_images/ellippi.png new file mode 100644 index 000000000000..87f1fbc48b47 Binary files /dev/null and b/latest/_images/ellippi.png differ diff --git a/latest/_images/gi.png b/latest/_images/gi.png new file mode 100644 index 000000000000..53e22d245760 Binary files /dev/null and b/latest/_images/gi.png differ diff --git a/latest/_images/gi_c.png b/latest/_images/gi_c.png new file mode 100644 index 000000000000..d627aeb7f9a3 Binary files /dev/null and b/latest/_images/gi_c.png differ diff --git a/latest/_images/hankel1.png b/latest/_images/hankel1.png new file mode 100644 index 000000000000..be950dc23d40 Binary files /dev/null and b/latest/_images/hankel1.png differ diff --git a/latest/_images/hankel1_c.png b/latest/_images/hankel1_c.png new file mode 100644 index 000000000000..8547d94a4ef6 Binary files /dev/null and b/latest/_images/hankel1_c.png differ diff --git a/latest/_images/hankel2.png b/latest/_images/hankel2.png new file mode 100644 index 000000000000..f3691442c5f0 Binary files /dev/null and b/latest/_images/hankel2.png differ diff --git a/latest/_images/hankel2_c.png b/latest/_images/hankel2_c.png new file mode 100644 index 000000000000..4eb83ba2d078 Binary files /dev/null and b/latest/_images/hankel2_c.png differ diff --git a/latest/_images/hermite.png b/latest/_images/hermite.png new file mode 100644 index 000000000000..9628dad86e1d Binary files /dev/null and b/latest/_images/hermite.png differ diff --git a/latest/_images/hi.png b/latest/_images/hi.png new file mode 100644 index 000000000000..e4b2ae972537 Binary files /dev/null and b/latest/_images/hi.png differ diff --git a/latest/_images/hi_c.png b/latest/_images/hi_c.png new file mode 100644 index 000000000000..bb580ee9d8ac Binary files /dev/null and b/latest/_images/hi_c.png differ diff --git a/latest/_images/ker.png b/latest/_images/ker.png new file mode 100644 index 000000000000..ed3bd11096ec Binary files /dev/null and b/latest/_images/ker.png differ diff --git a/latest/_images/kleinj.png b/latest/_images/kleinj.png new file mode 100644 index 000000000000..098c79adacff Binary files /dev/null and b/latest/_images/kleinj.png differ diff --git a/latest/_images/kleinj2.png b/latest/_images/kleinj2.png new file mode 100644 index 000000000000..87a618663e27 Binary files /dev/null and b/latest/_images/kleinj2.png differ diff --git a/latest/_images/laguerre.png b/latest/_images/laguerre.png new file mode 100644 index 000000000000..2bdc0c760692 Binary files /dev/null and b/latest/_images/laguerre.png differ diff --git a/latest/_images/lambertw.png b/latest/_images/lambertw.png new file mode 100644 index 000000000000..0d96a6265814 Binary files /dev/null and b/latest/_images/lambertw.png differ diff --git a/latest/_images/lambertw_c.png b/latest/_images/lambertw_c.png new file mode 100644 index 000000000000..4f23ee29c64e Binary files /dev/null and b/latest/_images/lambertw_c.png differ diff --git a/latest/_images/legendre.png b/latest/_images/legendre.png new file mode 100644 index 000000000000..a02e14ea8e46 Binary files /dev/null and b/latest/_images/legendre.png differ diff --git a/latest/_images/lommels1.png b/latest/_images/lommels1.png new file mode 100644 index 000000000000..555a71583970 Binary files /dev/null and b/latest/_images/lommels1.png differ diff --git a/latest/_images/lommels2.png b/latest/_images/lommels2.png new file mode 100644 index 000000000000..4b496808e954 Binary files /dev/null and b/latest/_images/lommels2.png differ diff --git a/latest/_images/manifold_testlatex.png b/latest/_images/manifold_testlatex.png new file mode 100644 index 000000000000..ca4dcef33b63 Binary files /dev/null and b/latest/_images/manifold_testlatex.png differ diff --git a/latest/_images/pcfd.png b/latest/_images/pcfd.png new file mode 100644 index 000000000000..966295e0e6b7 Binary files /dev/null and b/latest/_images/pcfd.png differ diff --git a/latest/_images/plot.png b/latest/_images/plot.png new file mode 100644 index 000000000000..f864c4648977 Binary files /dev/null and b/latest/_images/plot.png differ diff --git a/latest/_images/simple_test_latex_1.png b/latest/_images/simple_test_latex_1.png new file mode 100644 index 000000000000..37017c658683 Binary files /dev/null and b/latest/_images/simple_test_latex_1.png differ diff --git a/latest/_images/simple_test_latex_2.png b/latest/_images/simple_test_latex_2.png new file mode 100644 index 000000000000..c790b97400d5 Binary files /dev/null and b/latest/_images/simple_test_latex_2.png differ diff --git a/latest/_images/spherharm40.png b/latest/_images/spherharm40.png new file mode 100644 index 000000000000..b0a0cc7c994d Binary files /dev/null and b/latest/_images/spherharm40.png differ diff --git a/latest/_images/spherharm41.png b/latest/_images/spherharm41.png new file mode 100644 index 000000000000..25ff1e9fcde7 Binary files /dev/null and b/latest/_images/spherharm41.png differ diff --git a/latest/_images/spherharm42.png b/latest/_images/spherharm42.png new file mode 100644 index 000000000000..6ba62f79db40 Binary files /dev/null and b/latest/_images/spherharm42.png differ diff --git a/latest/_images/spherharm43.png b/latest/_images/spherharm43.png new file mode 100644 index 000000000000..d7eb4b2104c1 Binary files /dev/null and b/latest/_images/spherharm43.png differ diff --git a/latest/_images/spherharm44.png b/latest/_images/spherharm44.png new file mode 100644 index 000000000000..83a712d48717 Binary files /dev/null and b/latest/_images/spherharm44.png differ diff --git a/latest/_images/splot.png b/latest/_images/splot.png new file mode 100644 index 000000000000..df75c71e3bba Binary files /dev/null and b/latest/_images/splot.png differ diff --git a/latest/_modules/mpmath.html b/latest/_modules/mpmath.html new file mode 100644 index 000000000000..56f22393d947 --- /dev/null +++ b/latest/_modules/mpmath.html @@ -0,0 +1,569 @@ + + + + + + + + + + mpmath — SymPy 0.7.6.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    + +

    Source code for mpmath

    +__version__ = '0.18'
    +
    +from .usertools import monitor, timing
    +
    +from .ctx_fp import FPContext
    +from .ctx_mp import MPContext
    +from .ctx_iv import MPIntervalContext
    +
    +fp = FPContext()
    +mp = MPContext()
    +iv = MPIntervalContext()
    +
    +fp._mp = mp
    +mp._mp = mp
    +iv._mp = mp
    +mp._fp = fp
    +fp._fp = fp
    +mp._iv = iv
    +fp._iv = iv
    +iv._iv = iv
    +
    +# XXX: extremely bad pickle hack
    +from . import ctx_mp as _ctx_mp
    +_ctx_mp._mpf_module.mpf = mp.mpf
    +_ctx_mp._mpf_module.mpc = mp.mpc
    +
    +make_mpf = mp.make_mpf
    +make_mpc = mp.make_mpc
    +
    +extraprec = mp.extraprec
    +extradps = mp.extradps
    +workprec = mp.workprec
    +workdps = mp.workdps
    +autoprec = mp.autoprec
    +maxcalls = mp.maxcalls
    +memoize = mp.memoize
    +
    +mag = mp.mag
    +
    +bernfrac = mp.bernfrac
    +
    +qfrom = mp.qfrom
    +mfrom = mp.mfrom
    +kfrom = mp.kfrom
    +taufrom = mp.taufrom
    +qbarfrom = mp.qbarfrom
    +ellipfun = mp.ellipfun
    +jtheta = mp.jtheta
    +kleinj = mp.kleinj
    +
    +qp = mp.qp
    +qhyper = mp.qhyper
    +qgamma = mp.qgamma
    +qfac = mp.qfac
    +
    +nint_distance = mp.nint_distance
    +
    +plot = mp.plot
    +cplot = mp.cplot
    +splot = mp.splot
    +
    +odefun = mp.odefun
    +
    +jacobian = mp.jacobian
    +findroot = mp.findroot
    +multiplicity = mp.multiplicity
    +
    +isinf = mp.isinf
    +isnan = mp.isnan
    +isnormal = mp.isnormal
    +isint = mp.isint
    +isfinite = mp.isfinite
    +almosteq = mp.almosteq
    +nan = mp.nan
    +rand = mp.rand
    +
    +absmin = mp.absmin
    +absmax = mp.absmax
    +
    +fraction = mp.fraction
    +
    +linspace = mp.linspace
    +arange = mp.arange
    +
    +mpmathify = convert = mp.convert
    +mpc = mp.mpc
    +
    +mpi = iv._mpi
    +
    +nstr = mp.nstr
    +nprint = mp.nprint
    +chop = mp.chop
    +
    +fneg = mp.fneg
    +fadd = mp.fadd
    +fsub = mp.fsub
    +fmul = mp.fmul
    +fdiv = mp.fdiv
    +fprod = mp.fprod
    +
    +quad = mp.quad
    +quadgl = mp.quadgl
    +quadts = mp.quadts
    +quadosc = mp.quadosc
    +
    +pslq = mp.pslq
    +identify = mp.identify
    +findpoly = mp.findpoly
    +
    +richardson = mp.richardson
    +shanks = mp.shanks
    +levin = mp.levin
    +cohen_alt = mp.cohen_alt
    +nsum = mp.nsum
    +nprod = mp.nprod
    +difference = mp.difference
    +diff = mp.diff
    +diffs = mp.diffs
    +diffs_prod = mp.diffs_prod
    +diffs_exp = mp.diffs_exp
    +diffun = mp.diffun
    +differint = mp.differint
    +taylor = mp.taylor
    +pade = mp.pade
    +polyval = mp.polyval
    +polyroots = mp.polyroots
    +fourier = mp.fourier
    +fourierval = mp.fourierval
    +sumem = mp.sumem
    +sumap = mp.sumap
    +chebyfit = mp.chebyfit
    +limit = mp.limit
    +
    +matrix = mp.matrix
    +eye = mp.eye
    +diag = mp.diag
    +zeros = mp.zeros
    +ones = mp.ones
    +hilbert = mp.hilbert
    +randmatrix = mp.randmatrix
    +swap_row = mp.swap_row
    +extend = mp.extend
    +norm = mp.norm
    +mnorm = mp.mnorm
    +
    +lu_solve = mp.lu_solve
    +lu = mp.lu
    +qr = mp.qr
    +unitvector = mp.unitvector
    +inverse = mp.inverse
    +residual = mp.residual
    +qr_solve = mp.qr_solve
    +cholesky = mp.cholesky
    +cholesky_solve = mp.cholesky_solve
    +det = mp.det
    +cond = mp.cond
    +hessenberg = mp.hessenberg
    +schur = mp.schur
    +eig = mp.eig
    +eig_sort = mp.eig_sort
    +eigsy = mp.eigsy
    +eighe = mp.eighe
    +eigh = mp.eigh
    +svd_r = mp.svd_r
    +svd_c = mp.svd_c
    +svd = mp.svd
    +gauss_quadrature = mp.gauss_quadrature
    +
    +expm = mp.expm
    +sqrtm = mp.sqrtm
    +powm = mp.powm
    +logm = mp.logm
    +sinm = mp.sinm
    +cosm = mp.cosm
    +
    +mpf = mp.mpf
    +j = mp.j
    +exp = mp.exp
    +expj = mp.expj
    +expjpi = mp.expjpi
    +ln = mp.ln
    +im = mp.im
    +re = mp.re
    +inf = mp.inf
    +ninf = mp.ninf
    +sign = mp.sign
    +
    +eps = mp.eps
    +pi = mp.pi
    +ln2 = mp.ln2
    +ln10 = mp.ln10
    +phi = mp.phi
    +e = mp.e
    +euler = mp.euler
    +catalan = mp.catalan
    +khinchin = mp.khinchin
    +glaisher = mp.glaisher
    +apery = mp.apery
    +degree = mp.degree
    +twinprime = mp.twinprime
    +mertens = mp.mertens
    +
    +ldexp = mp.ldexp
    +frexp = mp.frexp
    +
    +fsum = mp.fsum
    +fdot = mp.fdot
    +
    +sqrt = mp.sqrt
    +cbrt = mp.cbrt
    +exp = mp.exp
    +ln = mp.ln
    +log = mp.log
    +log10 = mp.log10
    +power = mp.power
    +cos = mp.cos
    +sin = mp.sin
    +tan = mp.tan
    +cosh = mp.cosh
    +sinh = mp.sinh
    +tanh = mp.tanh
    +acos = mp.acos
    +asin = mp.asin
    +atan = mp.atan
    +asinh = mp.asinh
    +acosh = mp.acosh
    +atanh = mp.atanh
    +sec = mp.sec
    +csc = mp.csc
    +cot = mp.cot
    +sech = mp.sech
    +csch = mp.csch
    +coth = mp.coth
    +asec = mp.asec
    +acsc = mp.acsc
    +acot = mp.acot
    +asech = mp.asech
    +acsch = mp.acsch
    +acoth = mp.acoth
    +cospi = mp.cospi
    +sinpi = mp.sinpi
    +sinc = mp.sinc
    +sincpi = mp.sincpi
    +cos_sin = mp.cos_sin
    +cospi_sinpi = mp.cospi_sinpi
    +fabs = mp.fabs
    +re = mp.re
    +im = mp.im
    +conj = mp.conj
    +floor = mp.floor
    +ceil = mp.ceil
    +nint = mp.nint
    +frac = mp.frac
    +root = mp.root
    +nthroot = mp.nthroot
    +hypot = mp.hypot
    +fmod = mp.fmod
    +ldexp = mp.ldexp
    +frexp = mp.frexp
    +sign = mp.sign
    +arg = mp.arg
    +phase = mp.phase
    +polar = mp.polar
    +rect = mp.rect
    +degrees = mp.degrees
    +radians = mp.radians
    +atan2 = mp.atan2
    +fib = mp.fib
    +fibonacci = mp.fibonacci
    +lambertw = mp.lambertw
    +zeta = mp.zeta
    +altzeta = mp.altzeta
    +gamma = mp.gamma
    +rgamma = mp.rgamma
    +factorial = mp.factorial
    +fac = mp.fac
    +fac2 = mp.fac2
    +beta = mp.beta
    +betainc = mp.betainc
    +psi = mp.psi
    +#psi0 = mp.psi0
    +#psi1 = mp.psi1
    +#psi2 = mp.psi2
    +#psi3 = mp.psi3
    +polygamma = mp.polygamma
    +digamma = mp.digamma
    +#trigamma = mp.trigamma
    +#tetragamma = mp.tetragamma
    +#pentagamma = mp.pentagamma
    +harmonic = mp.harmonic
    +bernoulli = mp.bernoulli
    +bernfrac = mp.bernfrac
    +stieltjes = mp.stieltjes
    +hurwitz = mp.hurwitz
    +dirichlet = mp.dirichlet
    +bernpoly = mp.bernpoly
    +eulerpoly = mp.eulerpoly
    +eulernum = mp.eulernum
    +polylog = mp.polylog
    +clsin = mp.clsin
    +clcos = mp.clcos
    +gammainc = mp.gammainc
    +gammaprod = mp.gammaprod
    +binomial = mp.binomial
    +rf = mp.rf
    +ff = mp.ff
    +hyper = mp.hyper
    +hyp0f1 = mp.hyp0f1
    +hyp1f1 = mp.hyp1f1
    +hyp1f2 = mp.hyp1f2
    +hyp2f1 = mp.hyp2f1
    +hyp2f2 = mp.hyp2f2
    +hyp2f0 = mp.hyp2f0
    +hyp2f3 = mp.hyp2f3
    +hyp3f2 = mp.hyp3f2
    +hyperu = mp.hyperu
    +hypercomb = mp.hypercomb
    +meijerg = mp.meijerg
    +appellf1 = mp.appellf1
    +appellf2 = mp.appellf2
    +appellf3 = mp.appellf3
    +appellf4 = mp.appellf4
    +hyper2d = mp.hyper2d
    +bihyper = mp.bihyper
    +erf = mp.erf
    +erfc = mp.erfc
    +erfi = mp.erfi
    +erfinv = mp.erfinv
    +npdf = mp.npdf
    +ncdf = mp.ncdf
    +expint = mp.expint
    +e1 = mp.e1
    +ei = mp.ei
    +li = mp.li
    +ci = mp.ci
    +si = mp.si
    +chi = mp.chi
    +shi = mp.shi
    +fresnels = mp.fresnels
    +fresnelc = mp.fresnelc
    +airyai = mp.airyai
    +airybi = mp.airybi
    +airyaizero = mp.airyaizero
    +airybizero = mp.airybizero
    +scorergi = mp.scorergi
    +scorerhi = mp.scorerhi
    +ellipk = mp.ellipk
    +ellipe = mp.ellipe
    +ellipf = mp.ellipf
    +ellippi = mp.ellippi
    +elliprc = mp.elliprc
    +elliprj = mp.elliprj
    +elliprf = mp.elliprf
    +elliprd = mp.elliprd
    +elliprg = mp.elliprg
    +agm = mp.agm
    +jacobi = mp.jacobi
    +chebyt = mp.chebyt
    +chebyu = mp.chebyu
    +legendre = mp.legendre
    +legenp = mp.legenp
    +legenq = mp.legenq
    +hermite = mp.hermite
    +pcfd = mp.pcfd
    +pcfu = mp.pcfu
    +pcfv = mp.pcfv
    +pcfw = mp.pcfw
    +gegenbauer = mp.gegenbauer
    +laguerre = mp.laguerre
    +spherharm = mp.spherharm
    +besselj = mp.besselj
    +j0 = mp.j0
    +j1 = mp.j1
    +besseli = mp.besseli
    +bessely = mp.bessely
    +besselk = mp.besselk
    +besseljzero = mp.besseljzero
    +besselyzero = mp.besselyzero
    +hankel1 = mp.hankel1
    +hankel2 = mp.hankel2
    +struveh = mp.struveh
    +struvel = mp.struvel
    +angerj = mp.angerj
    +webere = mp.webere
    +lommels1 = mp.lommels1
    +lommels2 = mp.lommels2
    +whitm = mp.whitm
    +whitw = mp.whitw
    +ber = mp.ber
    +bei = mp.bei
    +ker = mp.ker
    +kei = mp.kei
    +coulombc = mp.coulombc
    +coulombf = mp.coulombf
    +coulombg = mp.coulombg
    +lambertw = mp.lambertw
    +barnesg = mp.barnesg
    +superfac = mp.superfac
    +hyperfac = mp.hyperfac
    +loggamma = mp.loggamma
    +siegeltheta = mp.siegeltheta
    +siegelz = mp.siegelz
    +grampoint = mp.grampoint
    +zetazero = mp.zetazero
    +riemannr = mp.riemannr
    +primepi = mp.primepi
    +primepi2 = mp.primepi2
    +primezeta = mp.primezeta
    +bell = mp.bell
    +polyexp = mp.polyexp
    +expm1 = mp.expm1
    +powm1 = mp.powm1
    +unitroots = mp.unitroots
    +cyclotomic = mp.cyclotomic
    +mangoldt = mp.mangoldt
    +secondzeta = mp.secondzeta
    +nzeros = mp.nzeros
    +backlunds = mp.backlunds
    +lerchphi = mp.lerchphi
    +stirling1 = mp.stirling1
    +stirling2 = mp.stirling2
    +
    +# be careful when changing this name, don't use test*!
    +def runtests():
    +    """
    +    Run all mpmath tests and print output.
    +    """
    +    import os.path
    +    from inspect import getsourcefile
    +    from .tests import runtests as tests
    +    testdir = os.path.dirname(os.path.abspath(getsourcefile(tests)))
    +    importdir = os.path.abspath(testdir + '/../..')
    +    tests.testit(importdir, testdir)
    +
    +def doctests(filter=[]):
    +    try:
    +        import psyco; psyco.full()
    +    except ImportError:
    +        pass
    +    import sys
    +    from timeit import default_timer as clock
    +    for i, arg in enumerate(sys.argv):
    +        if '__init__.py' in arg:
    +            filter = [sn for sn in sys.argv[i+1:] if not sn.startswith("-")]
    +            break
    +    import doctest
    +    globs = globals().copy()
    +    for obj in globs: #sorted(globs.keys()):
    +        if filter:
    +            if not sum([pat in obj for pat in filter]):
    +                continue
    +        sys.stdout.write(str(obj) + " ")
    +        sys.stdout.flush()
    +        t1 = clock()
    +        doctest.run_docstring_examples(globs[obj], {}, verbose=("-v" in sys.argv))
    +        t2 = clock()
    +        print(round(t2-t1, 3))
    +
    +if __name__ == '__main__':
    +    doctests()
    +
    + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/latest/_modules/mpmath/calculus/optimization.html b/latest/_modules/mpmath/calculus/optimization.html new file mode 100644 index 000000000000..e3d16b6d4c85 --- /dev/null +++ b/latest/_modules/mpmath/calculus/optimization.html @@ -0,0 +1,1203 @@ + + + + + + + + + + mpmath.calculus.optimization — SymPy 0.7.6.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    + +

    Source code for mpmath.calculus.optimization

    +from copy import copy
    +
    +from ..libmp.backend import xrange, print_
    +
    +class OptimizationMethods(object):
    +    def __init__(ctx):
    +        pass
    +
    +##############
    +# 1D-SOLVERS #
    +##############
    +
    +
    [docs]class Newton: + """ + 1d-solver generating pairs of approximative root and error. + + Needs starting points x0 close to the root. + + Pro: + + * converges fast + * sometimes more robust than secant with bad second starting point + + Contra: + + * converges slowly for multiple roots + * needs first derivative + * 2 function evaluations per iteration + """ + maxsteps = 20 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if len(x0) == 1: + self.x0 = x0[0] + else: + raise ValueError('expected 1 starting point, got %i' % len(x0)) + self.f = f + if not 'df' in kwargs: + def df(x): + return self.ctx.diff(f, x) + else: + df = kwargs['df'] + self.df = df + + def __iter__(self): + f = self.f + df = self.df + x0 = self.x0 + while True: + x1 = x0 - f(x0) / df(x0) + error = abs(x1 - x0) + x0 = x1 + yield (x1, error) +
    +
    [docs]class Secant: + """ + 1d-solver generating pairs of approximative root and error. + + Needs starting points x0 and x1 close to the root. + x1 defaults to x0 + 0.25. + + Pro: + + * converges fast + + Contra: + + * converges slowly for multiple roots + """ + maxsteps = 30 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if len(x0) == 1: + self.x0 = x0[0] + self.x1 = self.x0 + 0.25 + elif len(x0) == 2: + self.x0 = x0[0] + self.x1 = x0[1] + else: + raise ValueError('expected 1 or 2 starting points, got %i' % len(x0)) + self.f = f + + def __iter__(self): + f = self.f + x0 = self.x0 + x1 = self.x1 + f0 = f(x0) + while True: + f1 = f(x1) + l = x1 - x0 + if not l: + break + s = (f1 - f0) / l + if not s: + break + x0, x1 = x1, x1 - f1/s + f0 = f1 + yield x1, abs(l) +
    +
    [docs]class MNewton: + """ + 1d-solver generating pairs of approximative root and error. + + Needs starting point x0 close to the root. + Uses modified Newton's method that converges fast regardless of the + multiplicity of the root. + + Pro: + + * converges fast for multiple roots + + Contra: + + * needs first and second derivative of f + * 3 function evaluations per iteration + """ + maxsteps = 20 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if not len(x0) == 1: + raise ValueError('expected 1 starting point, got %i' % len(x0)) + self.x0 = x0[0] + self.f = f + if not 'df' in kwargs: + def df(x): + return self.ctx.diff(f, x) + else: + df = kwargs['df'] + self.df = df + if not 'd2f' in kwargs: + def d2f(x): + return self.ctx.diff(df, x) + else: + d2f = kwargs['df'] + self.d2f = d2f + + def __iter__(self): + x = self.x0 + f = self.f + df = self.df + d2f = self.d2f + while True: + prevx = x + fx = f(x) + if fx == 0: + break + dfx = df(x) + d2fx = d2f(x) + # x = x - F(x)/F'(x) with F(x) = f(x)/f'(x) + x -= fx / (dfx - fx * d2fx / dfx) + error = abs(x - prevx) + yield x, error +
    +
    [docs]class Halley: + """ + 1d-solver generating pairs of approximative root and error. + + Needs a starting point x0 close to the root. + Uses Halley's method with cubic convergence rate. + + Pro: + + * converges even faster the Newton's method + * useful when computing with *many* digits + + Contra: + + * needs first and second derivative of f + * 3 function evaluations per iteration + * converges slowly for multiple roots + """ + + maxsteps = 20 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if not len(x0) == 1: + raise ValueError('expected 1 starting point, got %i' % len(x0)) + self.x0 = x0[0] + self.f = f + if not 'df' in kwargs: + def df(x): + return self.ctx.diff(f, x) + else: + df = kwargs['df'] + self.df = df + if not 'd2f' in kwargs: + def d2f(x): + return self.ctx.diff(df, x) + else: + d2f = kwargs['df'] + self.d2f = d2f + + def __iter__(self): + x = self.x0 + f = self.f + df = self.df + d2f = self.d2f + while True: + prevx = x + fx = f(x) + dfx = df(x) + d2fx = d2f(x) + x -= 2*fx*dfx / (2*dfx**2 - fx*d2fx) + error = abs(x - prevx) + yield x, error +
    +
    [docs]class Muller: + """ + 1d-solver generating pairs of approximative root and error. + + Needs starting points x0, x1 and x2 close to the root. + x1 defaults to x0 + 0.25; x2 to x1 + 0.25. + Uses Muller's method that converges towards complex roots. + + Pro: + + * converges fast (somewhat faster than secant) + * can find complex roots + + Contra: + + * converges slowly for multiple roots + * may have complex values for real starting points and real roots + + http://en.wikipedia.org/wiki/Muller's_method + """ + maxsteps = 30 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if len(x0) == 1: + self.x0 = x0[0] + self.x1 = self.x0 + 0.25 + self.x2 = self.x1 + 0.25 + elif len(x0) == 2: + self.x0 = x0[0] + self.x1 = x0[1] + self.x2 = self.x1 + 0.25 + elif len(x0) == 3: + self.x0 = x0[0] + self.x1 = x0[1] + self.x2 = x0[2] + else: + raise ValueError('expected 1, 2 or 3 starting points, got %i' + % len(x0)) + self.f = f + self.verbose = kwargs['verbose'] + + def __iter__(self): + f = self.f + x0 = self.x0 + x1 = self.x1 + x2 = self.x2 + fx0 = f(x0) + fx1 = f(x1) + fx2 = f(x2) + while True: + # TODO: maybe refactoring with function for divided differences + # calculate divided differences + fx2x1 = (fx1 - fx2) / (x1 - x2) + fx2x0 = (fx0 - fx2) / (x0 - x2) + fx1x0 = (fx0 - fx1) / (x0 - x1) + w = fx2x1 + fx2x0 - fx1x0 + fx2x1x0 = (fx1x0 - fx2x1) / (x0 - x2) + if w == 0 and fx2x1x0 == 0: + if self.verbose: + print_('canceled with') + print_('x0 =', x0, ', x1 =', x1, 'and x2 =', x2) + break + x0 = x1 + fx0 = fx1 + x1 = x2 + fx1 = fx2 + # denominator should be as large as possible => choose sign + r = self.ctx.sqrt(w**2 - 4*fx2*fx2x1x0) + if abs(w - r) > abs(w + r): + r = -r + x2 -= 2*fx2 / (w + r) + fx2 = f(x2) + error = abs(x2 - x1) + yield x2, error + +# TODO: consider raising a ValueError when there's no sign change in a and b
    +
    [docs]class Bisection: + """ + 1d-solver generating pairs of approximative root and error. + + Uses bisection method to find a root of f in [a, b]. + Might fail for multiple roots (needs sign change). + + Pro: + + * robust and reliable + + Contra: + + * converges slowly + * needs sign change + """ + maxsteps = 100 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if len(x0) != 2: + raise ValueError('expected interval of 2 points, got %i' % len(x0)) + self.f = f + self.a = x0[0] + self.b = x0[1] + + def __iter__(self): + f = self.f + a = self.a + b = self.b + l = b - a + fb = f(b) + while True: + m = self.ctx.ldexp(a + b, -1) + fm = f(m) + sign = fm * fb + if sign < 0: + a = m + elif sign > 0: + b = m + fb = fm + else: + yield m, self.ctx.zero + l /= 2 + yield (a + b)/2, abs(l) +
    +def _getm(method): + """ + Return a function to calculate m for Illinois-like methods. + """ + if method == 'illinois': + def getm(fz, fb): + return 0.5 + elif method == 'pegasus': + def getm(fz, fb): + return fb/(fb + fz) + elif method == 'anderson': + def getm(fz, fb): + m = 1 - fz/fb + if m > 0: + return m + else: + return 0.5 + else: + raise ValueError("method '%s' not recognized" % method) + return getm + +
    [docs]class Illinois: + """ + 1d-solver generating pairs of approximative root and error. + + Uses Illinois method or similar to find a root of f in [a, b]. + Might fail for multiple roots (needs sign change). + Combines bisect with secant (improved regula falsi). + + The only difference between the methods is the scaling factor m, which is + used to ensure convergence (you can choose one using the 'method' keyword): + + Illinois method ('illinois'): + m = 0.5 + + Pegasus method ('pegasus'): + m = fb/(fb + fz) + + Anderson-Bjoerk method ('anderson'): + m = 1 - fz/fb if positive else 0.5 + + Pro: + + * converges very fast + + Contra: + + * has problems with multiple roots + * needs sign change + """ + maxsteps = 30 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if len(x0) != 2: + raise ValueError('expected interval of 2 points, got %i' % len(x0)) + self.a = x0[0] + self.b = x0[1] + self.f = f + self.tol = kwargs['tol'] + self.verbose = kwargs['verbose'] + self.method = kwargs.get('method', 'illinois') + self.getm = _getm(self.method) + if self.verbose: + print_('using %s method' % self.method) + + def __iter__(self): + method = self.method + f = self.f + a = self.a + b = self.b + fa = f(a) + fb = f(b) + m = None + while True: + l = b - a + if l == 0: + break + s = (fb - fa) / l + z = a - fa/s + fz = f(z) + if abs(fz) < self.tol: + # TODO: better condition (when f is very flat) + if self.verbose: + print_('canceled with z =', z) + yield z, l + break + if fz * fb < 0: # root in [z, b] + a = b + fa = fb + b = z + fb = fz + else: # root in [a, z] + m = self.getm(fz, fb) + b = z + fb = fz + fa = m*fa # scale down to ensure convergence + if self.verbose and m and not method == 'illinois': + print_('m:', m) + yield (a + b)/2, abs(l) +
    +
    [docs]def Pegasus(*args, **kwargs): + """ + 1d-solver generating pairs of approximative root and error. + + Uses Pegasus method to find a root of f in [a, b]. + Wrapper for illinois to use method='pegasus'. + """ + kwargs['method'] = 'pegasus' + return Illinois(*args, **kwargs) +
    +
    [docs]def Anderson(*args, **kwargs): + """ + 1d-solver generating pairs of approximative root and error. + + Uses Anderson-Bjoerk method to find a root of f in [a, b]. + Wrapper for illinois to use method='pegasus'. + """ + kwargs['method'] = 'anderson' + return Illinois(*args, **kwargs) + +# TODO: check whether it's possible to combine it with Illinois stuff
    +
    [docs]class Ridder: + """ + 1d-solver generating pairs of approximative root and error. + + Ridders' method to find a root of f in [a, b]. + Is told to perform as well as Brent's method while being simpler. + + Pro: + + * very fast + * simpler than Brent's method + + Contra: + + * two function evaluations per step + * has problems with multiple roots + * needs sign change + + http://en.wikipedia.org/wiki/Ridders'_method + """ + maxsteps = 30 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + self.f = f + if len(x0) != 2: + raise ValueError('expected interval of 2 points, got %i' % len(x0)) + self.x1 = x0[0] + self.x2 = x0[1] + self.verbose = kwargs['verbose'] + self.tol = kwargs['tol'] + + def __iter__(self): + ctx = self.ctx + f = self.f + x1 = self.x1 + fx1 = f(x1) + x2 = self.x2 + fx2 = f(x2) + while True: + x3 = 0.5*(x1 + x2) + fx3 = f(x3) + x4 = x3 + (x3 - x1) * ctx.sign(fx1 - fx2) * fx3 / ctx.sqrt(fx3**2 - fx1*fx2) + fx4 = f(x4) + if abs(fx4) < self.tol: + # TODO: better condition (when f is very flat) + if self.verbose: + print_('canceled with f(x4) =', fx4) + yield x4, abs(x1 - x2) + break + if fx4 * fx2 < 0: # root in [x4, x2] + x1 = x4 + fx1 = fx4 + else: # root in [x1, x4] + x2 = x4 + fx2 = fx4 + error = abs(x1 - x2) + yield (x1 + x2)/2, error +
    +
    [docs]class ANewton: + """ + EXPERIMENTAL 1d-solver generating pairs of approximative root and error. + + Uses Newton's method modified to use Steffensens method when convergence is + slow. (I.e. for multiple roots.) + """ + maxsteps = 20 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if not len(x0) == 1: + raise ValueError('expected 1 starting point, got %i' % len(x0)) + self.x0 = x0[0] + self.f = f + if not 'df' in kwargs: + def df(x): + return self.ctx.diff(f, x) + else: + df = kwargs['df'] + self.df = df + def phi(x): + return x - f(x) / df(x) + self.phi = phi + self.verbose = kwargs['verbose'] + + def __iter__(self): + x0 = self.x0 + f = self.f + df = self.df + phi = self.phi + error = 0 + counter = 0 + while True: + prevx = x0 + try: + x0 = phi(x0) + except ZeroDivisionError: + if self.verbose: + print_('ZeroDivisionError: canceled with x =', x0) + break + preverror = error + error = abs(prevx - x0) + # TODO: decide not to use convergence acceleration + if error and abs(error - preverror) / error < 1: + if self.verbose: + print_('converging slowly') + counter += 1 + if counter >= 3: + # accelerate convergence + phi = steffensen(phi) + counter = 0 + if self.verbose: + print_('accelerating convergence') + yield x0, error + +# TODO: add Brent + +############################ +# MULTIDIMENSIONAL SOLVERS # +############################ +
    +def jacobian(ctx, f, x): + """ + Calculate the Jacobian matrix of a function at the point x0. + + This is the first derivative of a vectorial function: + + f : R^m -> R^n with m >= n + """ + x = ctx.matrix(x) + h = ctx.sqrt(ctx.eps) + fx = ctx.matrix(f(*x)) + m = len(fx) + n = len(x) + J = ctx.matrix(m, n) + for j in xrange(n): + xj = x.copy() + xj[j] += h + Jj = (ctx.matrix(f(*xj)) - fx) / h + for i in xrange(m): + J[i,j] = Jj[i] + return J + +# TODO: test with user-specified jacobian matrix, support force_type +
    [docs]class MDNewton: + """ + Find the root of a vector function numerically using Newton's method. + + f is a vector function representing a nonlinear equation system. + + x0 is the starting point close to the root. + + J is a function returning the Jacobian matrix for a point. + + Supports overdetermined systems. + + Use the 'norm' keyword to specify which norm to use. Defaults to max-norm. + The function to calculate the Jacobian matrix can be given using the + keyword 'J'. Otherwise it will be calculated numerically. + + Please note that this method converges only locally. Especially for high- + dimensional systems it is not trivial to find a good starting point being + close enough to the root. + + It is recommended to use a faster, low-precision solver from SciPy [1] or + OpenOpt [2] to get an initial guess. Afterwards you can use this method for + root-polishing to any precision. + + [1] http://scipy.org + + [2] http://openopt.org/Welcome + """ + maxsteps = 10 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + self.f = f + if isinstance(x0, (tuple, list)): + x0 = ctx.matrix(x0) + assert x0.cols == 1, 'need a vector' + self.x0 = x0 + if 'J' in kwargs: + self.J = kwargs['J'] + else: + def J(*x): + return ctx.jacobian(f, x) + self.J = J + self.norm = kwargs['norm'] + self.verbose = kwargs['verbose'] + + def __iter__(self): + f = self.f + x0 = self.x0 + norm = self.norm + J = self.J + fx = self.ctx.matrix(f(*x0)) + fxnorm = norm(fx) + cancel = False + while not cancel: + # get direction of descent + fxn = -fx + Jx = J(*x0) + s = self.ctx.lu_solve(Jx, fxn) + if self.verbose: + print_('Jx:') + print_(Jx) + print_('s:', s) + # damping step size TODO: better strategy (hard task) + l = self.ctx.one + x1 = x0 + s + while True: + if x1 == x0: + if self.verbose: + print_("canceled, won't get more excact") + cancel = True + break + fx = self.ctx.matrix(f(*x1)) + newnorm = norm(fx) + if newnorm < fxnorm: + # new x accepted + fxnorm = newnorm + x0 = x1 + break + l /= 2 + x1 = x0 + l*s + yield (x0, fxnorm) + +############# +# UTILITIES # +############# +
    +str2solver = {'newton':Newton, 'secant':Secant,'mnewton':MNewton, + 'halley':Halley, 'muller':Muller, 'bisect':Bisection, + 'illinois':Illinois, 'pegasus':Pegasus, 'anderson':Anderson, + 'ridder':Ridder, 'anewton':ANewton, 'mdnewton':MDNewton} + +def findroot(ctx, f, x0, solver=Secant, tol=None, verbose=False, verify=True, **kwargs): + r""" + Find a solution to `f(x) = 0`, using *x0* as starting point or + interval for *x*. + + Multidimensional overdetermined systems are supported. + You can specify them using a function or a list of functions. + + If the found root does not satisfy `|f(x)^2 < \mathrm{tol}|`, + an exception is raised (this can be disabled with *verify=False*). + + **Arguments** + + *f* + one dimensional function + *x0* + starting point, several starting points or interval (depends on solver) + *tol* + the returned solution has an error smaller than this + *verbose* + print additional information for each iteration if true + *verify* + verify the solution and raise a ValueError if `|f(x) > \mathrm{tol}|` + *solver* + a generator for *f* and *x0* returning approximative solution and error + *maxsteps* + after how many steps the solver will cancel + *df* + first derivative of *f* (used by some solvers) + *d2f* + second derivative of *f* (used by some solvers) + *multidimensional* + force multidimensional solving + *J* + Jacobian matrix of *f* (used by multidimensional solvers) + *norm* + used vector norm (used by multidimensional solvers) + + solver has to be callable with ``(f, x0, **kwargs)`` and return an generator + yielding pairs of approximative solution and estimated error (which is + expected to be positive). + You can use the following string aliases: + 'secant', 'mnewton', 'halley', 'muller', 'illinois', 'pegasus', 'anderson', + 'ridder', 'anewton', 'bisect' + + See mpmath.optimization for their documentation. + + **Examples** + + The function :func:`~mpmath.findroot` locates a root of a given function using the + secant method by default. A simple example use of the secant method is to + compute `\pi` as the root of `\sin x` closest to `x_0 = 3`:: + + >>> from sympy.mpmath import * + >>> mp.dps = 30; mp.pretty = True + >>> findroot(sin, 3) + 3.14159265358979323846264338328 + + The secant method can be used to find complex roots of analytic functions, + although it must in that case generally be given a nonreal starting value + (or else it will never leave the real line):: + + >>> mp.dps = 15 + >>> findroot(lambda x: x**3 + 2*x + 1, j) + (0.226698825758202 + 1.46771150871022j) + + A nice application is to compute nontrivial roots of the Riemann zeta + function with many digits (good initial values are needed for convergence):: + + >>> mp.dps = 30 + >>> findroot(zeta, 0.5+14j) + (0.5 + 14.1347251417346937904572519836j) + + The secant method can also be used as an optimization algorithm, by passing + it a derivative of a function. The following example locates the positive + minimum of the gamma function:: + + >>> mp.dps = 20 + >>> findroot(lambda x: diff(gamma, x), 1) + 1.4616321449683623413 + + Finally, a useful application is to compute inverse functions, such as the + Lambert W function which is the inverse of `w e^w`, given the first + term of the solution's asymptotic expansion as the initial value. In basic + cases, this gives identical results to mpmath's built-in ``lambertw`` + function:: + + >>> def lambert(x): + ... return findroot(lambda w: w*exp(w) - x, log(1+x)) + ... + >>> mp.dps = 15 + >>> lambert(1); lambertw(1) + 0.567143290409784 + 0.567143290409784 + >>> lambert(1000); lambert(1000) + 5.2496028524016 + 5.2496028524016 + + Multidimensional functions are also supported:: + + >>> f = [lambda x1, x2: x1**2 + x2, + ... lambda x1, x2: 5*x1**2 - 3*x1 + 2*x2 - 3] + >>> findroot(f, (0, 0)) + [-0.618033988749895] + [-0.381966011250105] + >>> findroot(f, (10, 10)) + [ 1.61803398874989] + [-2.61803398874989] + + You can verify this by solving the system manually. + + Please note that the following (more general) syntax also works:: + + >>> def f(x1, x2): + ... return x1**2 + x2, 5*x1**2 - 3*x1 + 2*x2 - 3 + ... + >>> findroot(f, (0, 0)) + [-0.618033988749895] + [-0.381966011250105] + + + **Multiple roots** + + For multiple roots all methods of the Newtonian family (including secant) + converge slowly. Consider this example:: + + >>> f = lambda x: (x - 1)**99 + >>> findroot(f, 0.9, verify=False) + 0.918073542444929 + + Even for a very close starting point the secant method converges very + slowly. Use ``verbose=True`` to illustrate this. + + It is possible to modify Newton's method to make it converge regardless of + the root's multiplicity:: + + >>> findroot(f, -10, solver='mnewton') + 1.0 + + This variant uses the first and second derivative of the function, which is + not very efficient. + + Alternatively you can use an experimental Newtonian solver that keeps track + of the speed of convergence and accelerates it using Steffensen's method if + necessary:: + + >>> findroot(f, -10, solver='anewton', verbose=True) + x: -9.88888888888888888889 + error: 0.111111111111111111111 + converging slowly + x: -9.77890011223344556678 + error: 0.10998877665544332211 + converging slowly + x: -9.67002233332199662166 + error: 0.108877778911448945119 + converging slowly + accelerating convergence + x: -9.5622443299551077669 + error: 0.107778003366888854764 + converging slowly + x: 0.99999999999999999214 + error: 10.562244329955107759 + x: 1.0 + error: 7.8598304758094664213e-18 + ZeroDivisionError: canceled with x = 1.0 + 1.0 + + **Complex roots** + + For complex roots it's recommended to use Muller's method as it converges + even for real starting points very fast:: + + >>> findroot(lambda x: x**4 + x + 1, (0, 1, 2), solver='muller') + (0.727136084491197 + 0.934099289460529j) + + + **Intersection methods** + + When you need to find a root in a known interval, it's highly recommended to + use an intersection-based solver like ``'anderson'`` or ``'ridder'``. + Usually they converge faster and more reliable. They have however problems + with multiple roots and usually need a sign change to find a root:: + + >>> findroot(lambda x: x**3, (-1, 1), solver='anderson') + 0.0 + + Be careful with symmetric functions:: + + >>> findroot(lambda x: x**2, (-1, 1), solver='anderson') #doctest:+ELLIPSIS + Traceback (most recent call last): + ... + ZeroDivisionError + + It fails even for better starting points, because there is no sign change:: + + >>> findroot(lambda x: x**2, (-1, .5), solver='anderson') + Traceback (most recent call last): + ... + ValueError: Could not find root within given tolerance. (1 > 2.1684e-19) + Try another starting point or tweak arguments. + + """ + prec = ctx.prec + try: + ctx.prec += 20 + + # initialize arguments + if tol is None: + tol = ctx.eps * 2**10 + + kwargs['verbose'] = kwargs.get('verbose', verbose) + + if 'd1f' in kwargs: + kwargs['df'] = kwargs['d1f'] + + kwargs['tol'] = tol + if isinstance(x0, (list, tuple)): + x0 = [ctx.convert(x) for x in x0] + else: + x0 = [ctx.convert(x0)] + + if isinstance(solver, str): + try: + solver = str2solver[solver] + except KeyError: + raise ValueError('could not recognize solver') + + # accept list of functions + if isinstance(f, (list, tuple)): + f2 = copy(f) + def tmp(*args): + return [fn(*args) for fn in f2] + f = tmp + + # detect multidimensional functions + try: + fx = f(*x0) + multidimensional = isinstance(fx, (list, tuple, ctx.matrix)) + except TypeError: + fx = f(x0[0]) + multidimensional = False + if 'multidimensional' in kwargs: + multidimensional = kwargs['multidimensional'] + if multidimensional: + # only one multidimensional solver available at the moment + solver = MDNewton + if not 'norm' in kwargs: + norm = lambda x: ctx.norm(x, 'inf') + kwargs['norm'] = norm + else: + norm = kwargs['norm'] + else: + norm = abs + + # happily return starting point if it's a root + if norm(fx) == 0: + if multidimensional: + return ctx.matrix(x0) + else: + return x0[0] + + # use solver + iterations = solver(ctx, f, x0, **kwargs) + if 'maxsteps' in kwargs: + maxsteps = kwargs['maxsteps'] + else: + maxsteps = iterations.maxsteps + i = 0 + for x, error in iterations: + if verbose: + print_('x: ', x) + print_('error:', error) + i += 1 + if error < tol * max(1, norm(x)) or i >= maxsteps: + break + if not isinstance(x, (list, tuple, ctx.matrix)): + xl = [x] + else: + xl = x + if verify and norm(f(*xl))**2 > tol: # TODO: better condition? + raise ValueError('Could not find root within given tolerance. ' + '(%g > %g)\n' + 'Try another starting point or tweak arguments.' + % (norm(f(*xl))**2, tol)) + return x + finally: + ctx.prec = prec + + +def multiplicity(ctx, f, root, tol=None, maxsteps=10, **kwargs): + """ + Return the multiplicity of a given root of f. + + Internally, numerical derivatives are used. This might be inefficient for + higher order derviatives. Due to this, ``multiplicity`` cancels after + evaluating 10 derivatives by default. You can be specify the n-th derivative + using the dnf keyword. + + >>> from sympy.mpmath import * + >>> multiplicity(lambda x: sin(x) - 1, pi/2) + 2 + + """ + if tol is None: + tol = ctx.eps ** 0.8 + kwargs['d0f'] = f + for i in xrange(maxsteps): + dfstr = 'd' + str(i) + 'f' + if dfstr in kwargs: + df = kwargs[dfstr] + else: + df = lambda x: ctx.diff(f, x, i) + if not abs(df(root)) < tol: + break + return i + +def steffensen(f): + """ + linear convergent function -> quadratic convergent function + + Steffensen's method for quadratic convergence of a linear converging + sequence. + Don not use it for higher rates of convergence. + It may even work for divergent sequences. + + Definition: + F(x) = (x*f(f(x)) - f(x)**2) / (f(f(x)) - 2*f(x) + x) + + Example + ....... + + You can use Steffensen's method to accelerate a fixpoint iteration of linear + (or less) convergence. + + x* is a fixpoint of the iteration x_{k+1} = phi(x_k) if x* = phi(x*). For + phi(x) = x**2 there are two fixpoints: 0 and 1. + + Let's try Steffensen's method: + + >>> f = lambda x: x**2 + >>> from sympy.mpmath.optimization import steffensen + >>> F = steffensen(f) + >>> for x in [0.5, 0.9, 2.0]: + ... fx = Fx = x + ... for i in xrange(10): + ... try: + ... fx = f(fx) + ... except OverflowError: + ... pass + ... try: + ... Fx = F(Fx) + ... except ZeroDivisionError: + ... pass + ... print '%20g %20g' % (fx, Fx) + 0.25 -0.5 + 0.0625 0.1 + 0.00390625 -0.0011236 + 1.52588e-005 1.41691e-009 + 2.32831e-010 -2.84465e-027 + 5.42101e-020 2.30189e-080 + 2.93874e-039 -1.2197e-239 + 8.63617e-078 0 + 7.45834e-155 0 + 5.56268e-309 0 + 0.81 1.02676 + 0.6561 1.00134 + 0.430467 1 + 0.185302 1 + 0.0343368 1 + 0.00117902 1 + 1.39008e-006 1 + 1.93233e-012 1 + 3.73392e-024 1 + 1.39421e-047 1 + 4 1.6 + 16 1.2962 + 256 1.10194 + 65536 1.01659 + 4.29497e+009 1.00053 + 1.84467e+019 1 + 3.40282e+038 1 + 1.15792e+077 1 + 1.34078e+154 1 + 1.34078e+154 1 + + Unmodified, the iteration converges only towards 0. Modified it converges + not only much faster, it converges even to the repelling fixpoint 1. + """ + def F(x): + fx = f(x) + ffx = f(fx) + return (x*ffx - fx**2) / (ffx - 2*fx + x) + return F + +OptimizationMethods.jacobian = jacobian +OptimizationMethods.findroot = findroot +OptimizationMethods.multiplicity = multiplicity + +if __name__ == '__main__': + import doctest + doctest.testmod() +
    + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/latest/_modules/mpmath/calculus/quadrature.html b/latest/_modules/mpmath/calculus/quadrature.html new file mode 100644 index 000000000000..397ae278ec8e --- /dev/null +++ b/latest/_modules/mpmath/calculus/quadrature.html @@ -0,0 +1,1120 @@ + + + + + + + + + + mpmath.calculus.quadrature — SymPy 0.7.6.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    + +

    Source code for mpmath.calculus.quadrature

    +import math
    +
    +from ..libmp.backend import xrange
    +
    +
    [docs]class QuadratureRule(object): + """ + Quadrature rules are implemented using this class, in order to + simplify the code and provide a common infrastructure + for tasks such as error estimation and node caching. + + You can implement a custom quadrature rule by subclassing + :class:`QuadratureRule` and implementing the appropriate + methods. The subclass can then be used by :func:`~mpmath.quad` by + passing it as the *method* argument. + + :class:`QuadratureRule` instances are supposed to be singletons. + :class:`QuadratureRule` therefore implements instance caching + in :func:`~mpmath.__new__`. + """ + + def __init__(self, ctx): + self.ctx = ctx + self.standard_cache = {} + self.transformed_cache = {} + self.interval_count = {} + +
    [docs] def clear(self): + """ + Delete cached node data. + """ + self.standard_cache = {} + self.transformed_cache = {} + self.interval_count = {} +
    +
    [docs] def calc_nodes(self, degree, prec, verbose=False): + r""" + Compute nodes for the standard interval `[-1, 1]`. Subclasses + should probably implement only this method, and use + :func:`~mpmath.get_nodes` method to retrieve the nodes. + """ + raise NotImplementedError +
    +
    [docs] def get_nodes(self, a, b, degree, prec, verbose=False): + """ + Return nodes for given interval, degree and precision. The + nodes are retrieved from a cache if already computed; + otherwise they are computed by calling :func:`~mpmath.calc_nodes` + and are then cached. + + Subclasses should probably not implement this method, + but just implement :func:`~mpmath.calc_nodes` for the actual + node computation. + """ + key = (a, b, degree, prec) + if key in self.transformed_cache: + return self.transformed_cache[key] + orig = self.ctx.prec + try: + self.ctx.prec = prec+20 + # Get nodes on standard interval + if (degree, prec) in self.standard_cache: + nodes = self.standard_cache[degree, prec] + else: + nodes = self.calc_nodes(degree, prec, verbose) + self.standard_cache[degree, prec] = nodes + # Transform to general interval + nodes = self.transform_nodes(nodes, a, b, verbose) + if key in self.interval_count: + self.transformed_cache[key] = nodes + else: + self.interval_count[key] = True + finally: + self.ctx.prec = orig + return nodes +
    +
    [docs] def transform_nodes(self, nodes, a, b, verbose=False): + r""" + Rescale standardized nodes (for `[-1, 1]`) to a general + interval `[a, b]`. For a finite interval, a simple linear + change of variables is used. Otherwise, the following + transformations are used: + + .. math :: + + [a, \infty] : t = \frac{1}{x} + (a-1) + + [-\infty, b] : t = (b+1) - \frac{1}{x} + + [-\infty, \infty] : t = \frac{x}{\sqrt{1-x^2}} + + """ + ctx = self.ctx + a = ctx.convert(a) + b = ctx.convert(b) + one = ctx.one + if (a, b) == (-one, one): + return nodes + half = ctx.mpf(0.5) + new_nodes = [] + if ctx.isinf(a) or ctx.isinf(b): + if (a, b) == (ctx.ninf, ctx.inf): + p05 = -half + for x, w in nodes: + x2 = x*x + px1 = one-x2 + spx1 = px1**p05 + x = x*spx1 + w *= spx1/px1 + new_nodes.append((x, w)) + elif a == ctx.ninf: + b1 = b+1 + for x, w in nodes: + u = 2/(x+one) + x = b1-u + w *= half*u**2 + new_nodes.append((x, w)) + elif b == ctx.inf: + a1 = a-1 + for x, w in nodes: + u = 2/(x+one) + x = a1+u + w *= half*u**2 + new_nodes.append((x, w)) + elif a == ctx.inf or b == ctx.ninf: + return [(x,-w) for (x,w) in self.transform_nodes(nodes, b, a, verbose)] + else: + raise NotImplementedError + else: + # Simple linear change of variables + C = (b-a)/2 + D = (b+a)/2 + for x, w in nodes: + new_nodes.append((D+C*x, C*w)) + return new_nodes +
    +
    [docs] def guess_degree(self, prec): + """ + Given a desired precision `p` in bits, estimate the degree `m` + of the quadrature required to accomplish full accuracy for + typical integrals. By default, :func:`~mpmath.quad` will perform up + to `m` iterations. The value of `m` should be a slight + overestimate, so that "slightly bad" integrals can be dealt + with automatically using a few extra iterations. On the + other hand, it should not be too big, so :func:`~mpmath.quad` can + quit within a reasonable amount of time when it is given + an "unsolvable" integral. + + The default formula used by :func:`~mpmath.guess_degree` is tuned + for both :class:`TanhSinh` and :class:`GaussLegendre`. + The output is roughly as follows: + + +---------+---------+ + | `p` | `m` | + +=========+=========+ + | 50 | 6 | + +---------+---------+ + | 100 | 7 | + +---------+---------+ + | 500 | 10 | + +---------+---------+ + | 3000 | 12 | + +---------+---------+ + + This formula is based purely on a limited amount of + experimentation and will sometimes be wrong. + """ + # Expected degree + # XXX: use mag + g = int(4 + max(0, self.ctx.log(prec/30.0, 2))) + # Reasonable "worst case" + g += 2 + return g +
    +
    [docs] def estimate_error(self, results, prec, epsilon): + r""" + Given results from integrations `[I_1, I_2, \ldots, I_k]` done + with a quadrature of rule of degree `1, 2, \ldots, k`, estimate + the error of `I_k`. + + For `k = 2`, we estimate `|I_{\infty}-I_2|` as `|I_2-I_1|`. + + For `k > 2`, we extrapolate `|I_{\infty}-I_k| \approx |I_{k+1}-I_k|` + from `|I_k-I_{k-1}|` and `|I_k-I_{k-2}|` under the assumption + that each degree increment roughly doubles the accuracy of + the quadrature rule (this is true for both :class:`TanhSinh` + and :class:`GaussLegendre`). The extrapolation formula is given + by Borwein, Bailey & Girgensohn. Although not very conservative, + this method seems to be very robust in practice. + """ + if len(results) == 2: + return abs(results[0]-results[1]) + try: + if results[-1] == results[-2] == results[-3]: + return self.ctx.zero + D1 = self.ctx.log(abs(results[-1]-results[-2]), 10) + D2 = self.ctx.log(abs(results[-1]-results[-3]), 10) + except ValueError: + return epsilon + D3 = -prec + D4 = min(0, max(D1**2/D2, 2*D1, D3)) + return self.ctx.mpf(10) ** int(D4) +
    +
    [docs] def summation(self, f, points, prec, epsilon, max_degree, verbose=False): + """ + Main integration function. Computes the 1D integral over + the interval specified by *points*. For each subinterval, + performs quadrature of degree from 1 up to *max_degree* + until :func:`~mpmath.estimate_error` signals convergence. + + :func:`~mpmath.summation` transforms each subintegration to + the standard interval and then calls :func:`~mpmath.sum_next`. + """ + ctx = self.ctx + I = err = ctx.zero + for i in xrange(len(points)-1): + a, b = points[i], points[i+1] + if a == b: + continue + # XXX: we could use a single variable transformation, + # but this is not good in practice. We get better accuracy + # by having 0 as an endpoint. + if (a, b) == (ctx.ninf, ctx.inf): + _f = f + f = lambda x: _f(-x) + _f(x) + a, b = (ctx.zero, ctx.inf) + results = [] + for degree in xrange(1, max_degree+1): + nodes = self.get_nodes(a, b, degree, prec, verbose) + if verbose: + print("Integrating from %s to %s (degree %s of %s)" % \ + (ctx.nstr(a), ctx.nstr(b), degree, max_degree)) + results.append(self.sum_next(f, nodes, degree, prec, results, verbose)) + if degree > 1: + err = self.estimate_error(results, prec, epsilon) + if err <= epsilon: + break + if verbose: + print("Estimated error:", ctx.nstr(err)) + I += results[-1] + if err > epsilon: + if verbose: + print("Failed to reach full accuracy. Estimated error:", ctx.nstr(err)) + return I, err +
    +
    [docs] def sum_next(self, f, nodes, degree, prec, previous, verbose=False): + r""" + Evaluates the step sum `\sum w_k f(x_k)` where the *nodes* list + contains the `(w_k, x_k)` pairs. + + :func:`~mpmath.summation` will supply the list *results* of + values computed by :func:`~mpmath.sum_next` at previous degrees, in + case the quadrature rule is able to reuse them. + """ + return self.ctx.fdot((w, f(x)) for (x,w) in nodes) + +
    +
    [docs]class TanhSinh(QuadratureRule): + r""" + This class implements "tanh-sinh" or "doubly exponential" + quadrature. This quadrature rule is based on the Euler-Maclaurin + integral formula. By performing a change of variables involving + nested exponentials / hyperbolic functions (hence the name), the + derivatives at the endpoints vanish rapidly. Since the error term + in the Euler-Maclaurin formula depends on the derivatives at the + endpoints, a simple step sum becomes extremely accurate. In + practice, this means that doubling the number of evaluation + points roughly doubles the number of accurate digits. + + Comparison to Gauss-Legendre: + * Initial computation of nodes is usually faster + * Handles endpoint singularities better + * Handles infinite integration intervals better + * Is slower for smooth integrands once nodes have been computed + + The implementation of the tanh-sinh algorithm is based on the + description given in Borwein, Bailey & Girgensohn, "Experimentation + in Mathematics - Computational Paths to Discovery", A K Peters, + 2003, pages 312-313. In the present implementation, a few + improvements have been made: + + * A more efficient scheme is used to compute nodes (exploiting + recurrence for the exponential function) + * The nodes are computed successively instead of all at once + + Various documents describing the algorithm are available online, e.g.: + + * http://crd-legacy.lbl.gov/~dhbailey/dhbpapers/dhb-tanh-sinh.pdf + * http://users.cs.dal.ca/~jborwein/tanh-sinh.pdf + """ + +
    [docs] def sum_next(self, f, nodes, degree, prec, previous, verbose=False): + """ + Step sum for tanh-sinh quadrature of degree `m`. We exploit the + fact that half of the abscissas at degree `m` are precisely the + abscissas from degree `m-1`. Thus reusing the result from + the previous level allows a 2x speedup. + """ + h = self.ctx.mpf(2)**(-degree) + # Abscissas overlap, so reusing saves half of the time + if previous: + S = previous[-1]/(h*2) + else: + S = self.ctx.zero + S += self.ctx.fdot((w,f(x)) for (x,w) in nodes) + return h*S +
    +
    [docs] def calc_nodes(self, degree, prec, verbose=False): + r""" + The abscissas and weights for tanh-sinh quadrature of degree + `m` are given by + + .. math:: + + x_k = \tanh(\pi/2 \sinh(t_k)) + + w_k = \pi/2 \cosh(t_k) / \cosh(\pi/2 \sinh(t_k))^2 + + where `t_k = t_0 + hk` for a step length `h \sim 2^{-m}`. The + list of nodes is actually infinite, but the weights die off so + rapidly that only a few are needed. + """ + ctx = self.ctx + nodes = [] + + extra = 20 + ctx.prec += extra + tol = ctx.ldexp(1, -prec-10) + pi4 = ctx.pi/4 + + # For simplicity, we work in steps h = 1/2^n, with the first point + # offset so that we can reuse the sum from the previous degree + + # We define degree 1 to include the "degree 0" steps, including + # the point x = 0. (It doesn't work well otherwise; not sure why.) + t0 = ctx.ldexp(1, -degree) + if degree == 1: + #nodes.append((mpf(0), pi4)) + #nodes.append((-mpf(0), pi4)) + nodes.append((ctx.zero, ctx.pi/2)) + h = t0 + else: + h = t0*2 + + # Since h is fixed, we can compute the next exponential + # by simply multiplying by exp(h) + expt0 = ctx.exp(t0) + a = pi4 * expt0 + b = pi4 / expt0 + udelta = ctx.exp(h) + urdelta = 1/udelta + + for k in xrange(0, 20*2**degree+1): + # Reference implementation: + # t = t0 + k*h + # x = tanh(pi/2 * sinh(t)) + # w = pi/2 * cosh(t) / cosh(pi/2 * sinh(t))**2 + + # Fast implementation. Note that c = exp(pi/2 * sinh(t)) + c = ctx.exp(a-b) + d = 1/c + co = (c+d)/2 + si = (c-d)/2 + x = si / co + w = (a+b) / co**2 + diff = abs(x-1) + if diff <= tol: + break + + nodes.append((x, w)) + nodes.append((-x, w)) + + a *= udelta + b *= urdelta + + if verbose and k % 300 == 150: + # Note: the number displayed is rather arbitrary. Should + # figure out how to print something that looks more like a + # percentage + print("Calculating nodes:", ctx.nstr(-ctx.log(diff, 10) / prec)) + + ctx.prec -= extra + return nodes + +
    +
    [docs]class GaussLegendre(QuadratureRule): + """ + This class implements Gauss-Legendre quadrature, which is + exceptionally efficient for polynomials and polynomial-like (i.e. + very smooth) integrands. + + The abscissas and weights are given by roots and values of + Legendre polynomials, which are the orthogonal polynomials + on `[-1, 1]` with respect to the unit weight + (see :func:`~mpmath.legendre`). + + In this implementation, we take the "degree" `m` of the quadrature + to denote a Gauss-Legendre rule of degree `3 \cdot 2^m` (following + Borwein, Bailey & Girgensohn). This way we get quadratic, rather + than linear, convergence as the degree is incremented. + + Comparison to tanh-sinh quadrature: + * Is faster for smooth integrands once nodes have been computed + * Initial computation of nodes is usually slower + * Handles endpoint singularities worse + * Handles infinite integration intervals worse + + """ + +
    [docs] def calc_nodes(self, degree, prec, verbose=False): + """ + Calculates the abscissas and weights for Gauss-Legendre + quadrature of degree of given degree (actually `3 \cdot 2^m`). + """ + ctx = self.ctx + # It is important that the epsilon is set lower than the + # "real" epsilon + epsilon = ctx.ldexp(1, -prec-8) + # Fairly high precision might be required for accurate + # evaluation of the roots + orig = ctx.prec + ctx.prec = int(prec*1.5) + if degree == 1: + x = ctx.mpf(3)/5 + w = ctx.mpf(5)/9 + nodes = [(-x,w),(ctx.zero,ctx.mpf(8)/9),(x,w)] + ctx.prec = orig + return nodes + nodes = [] + n = 3*2**(degree-1) + upto = n//2 + 1 + for j in xrange(1, upto): + # Asymptotic formula for the roots + r = ctx.mpf(math.cos(math.pi*(j-0.25)/(n+0.5))) + # Newton iteration + while 1: + t1, t2 = 1, 0 + # Evaluates the Legendre polynomial using its defining + # recurrence relation + for j1 in xrange(1,n+1): + t3, t2, t1 = t2, t1, ((2*j1-1)*r*t1 - (j1-1)*t2)/j1 + t4 = n*(r*t1- t2)/(r**2-1) + t5 = r + a = t1/t4 + r = r - a + if abs(a) < epsilon: + break + x = r + w = 2/((1-r**2)*t4**2) + if verbose and j % 30 == 15: + print("Computing nodes (%i of %i)" % (j, upto)) + nodes.append((x, w)) + nodes.append((-x, w)) + ctx.prec = orig + return nodes +
    +class QuadratureMethods(object): + + def __init__(ctx, *args, **kwargs): + ctx._gauss_legendre = GaussLegendre(ctx) + ctx._tanh_sinh = TanhSinh(ctx) + + def quad(ctx, f, *points, **kwargs): + r""" + Computes a single, double or triple integral over a given + 1D interval, 2D rectangle, or 3D cuboid. A basic example:: + + >>> from sympy.mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> quad(sin, [0, pi]) + 2.0 + + A basic 2D integral:: + + >>> f = lambda x, y: cos(x+y/2) + >>> quad(f, [-pi/2, pi/2], [0, pi]) + 4.0 + + **Interval format** + + The integration range for each dimension may be specified + using a list or tuple. Arguments are interpreted as follows: + + ``quad(f, [x1, x2])`` -- calculates + `\int_{x_1}^{x_2} f(x) \, dx` + + ``quad(f, [x1, x2], [y1, y2])`` -- calculates + `\int_{x_1}^{x_2} \int_{y_1}^{y_2} f(x,y) \, dy \, dx` + + ``quad(f, [x1, x2], [y1, y2], [z1, z2])`` -- calculates + `\int_{x_1}^{x_2} \int_{y_1}^{y_2} \int_{z_1}^{z_2} f(x,y,z) + \, dz \, dy \, dx` + + Endpoints may be finite or infinite. An interval descriptor + may also contain more than two points. In this + case, the integration is split into subintervals, between + each pair of consecutive points. This is useful for + dealing with mid-interval discontinuities, or integrating + over large intervals where the function is irregular or + oscillates. + + **Options** + + :func:`~mpmath.quad` recognizes the following keyword arguments: + + *method* + Chooses integration algorithm (described below). + *error* + If set to true, :func:`~mpmath.quad` returns `(v, e)` where `v` is the + integral and `e` is the estimated error. + *maxdegree* + Maximum degree of the quadrature rule to try before + quitting. + *verbose* + Print details about progress. + + **Algorithms** + + Mpmath presently implements two integration algorithms: tanh-sinh + quadrature and Gauss-Legendre quadrature. These can be selected + using *method='tanh-sinh'* or *method='gauss-legendre'* or by + passing the classes *method=TanhSinh*, *method=GaussLegendre*. + The functions :func:`~mpmath.quadts` and :func:`~mpmath.quadgl` are also available + as shortcuts. + + Both algorithms have the property that doubling the number of + evaluation points roughly doubles the accuracy, so both are ideal + for high precision quadrature (hundreds or thousands of digits). + + At high precision, computing the nodes and weights for the + integration can be expensive (more expensive than computing the + function values). To make repeated integrations fast, nodes + are automatically cached. + + The advantages of the tanh-sinh algorithm are that it tends to + handle endpoint singularities well, and that the nodes are cheap + to compute on the first run. For these reasons, it is used by + :func:`~mpmath.quad` as the default algorithm. + + Gauss-Legendre quadrature often requires fewer function + evaluations, and is therefore often faster for repeated use, but + the algorithm does not handle endpoint singularities as well and + the nodes are more expensive to compute. Gauss-Legendre quadrature + can be a better choice if the integrand is smooth and repeated + integrations are required (e.g. for multiple integrals). + + See the documentation for :class:`TanhSinh` and + :class:`GaussLegendre` for additional details. + + **Examples of 1D integrals** + + Intervals may be infinite or half-infinite. The following two + examples evaluate the limits of the inverse tangent function + (`\int 1/(1+x^2) = \tan^{-1} x`), and the Gaussian integral + `\int_{\infty}^{\infty} \exp(-x^2)\,dx = \sqrt{\pi}`:: + + >>> mp.dps = 15 + >>> quad(lambda x: 2/(x**2+1), [0, inf]) + 3.14159265358979 + >>> quad(lambda x: exp(-x**2), [-inf, inf])**2 + 3.14159265358979 + + Integrals can typically be resolved to high precision. + The following computes 50 digits of `\pi` by integrating the + area of the half-circle defined by `x^2 + y^2 \le 1`, + `-1 \le x \le 1`, `y \ge 0`:: + + >>> mp.dps = 50 + >>> 2*quad(lambda x: sqrt(1-x**2), [-1, 1]) + 3.1415926535897932384626433832795028841971693993751 + + One can just as well compute 1000 digits (output truncated):: + + >>> mp.dps = 1000 + >>> 2*quad(lambda x: sqrt(1-x**2), [-1, 1]) #doctest:+ELLIPSIS + 3.141592653589793238462643383279502884...216420198 + + Complex integrals are supported. The following computes + a residue at `z = 0` by integrating counterclockwise along the + diamond-shaped path from `1` to `+i` to `-1` to `-i` to `1`:: + + >>> mp.dps = 15 + >>> chop(quad(lambda z: 1/z, [1,j,-1,-j,1])) + (0.0 + 6.28318530717959j) + + **Examples of 2D and 3D integrals** + + Here are several nice examples of analytically solvable + 2D integrals (taken from MathWorld [1]) that can be evaluated + to high precision fairly rapidly by :func:`~mpmath.quad`:: + + >>> mp.dps = 30 + >>> f = lambda x, y: (x-1)/((1-x*y)*log(x*y)) + >>> quad(f, [0, 1], [0, 1]) + 0.577215664901532860606512090082 + >>> +euler + 0.577215664901532860606512090082 + + >>> f = lambda x, y: 1/sqrt(1+x**2+y**2) + >>> quad(f, [-1, 1], [-1, 1]) + 3.17343648530607134219175646705 + >>> 4*log(2+sqrt(3))-2*pi/3 + 3.17343648530607134219175646705 + + >>> f = lambda x, y: 1/(1-x**2 * y**2) + >>> quad(f, [0, 1], [0, 1]) + 1.23370055013616982735431137498 + >>> pi**2 / 8 + 1.23370055013616982735431137498 + + >>> quad(lambda x, y: 1/(1-x*y), [0, 1], [0, 1]) + 1.64493406684822643647241516665 + >>> pi**2 / 6 + 1.64493406684822643647241516665 + + Multiple integrals may be done over infinite ranges:: + + >>> mp.dps = 15 + >>> print(quad(lambda x,y: exp(-x-y), [0, inf], [1, inf])) + 0.367879441171442 + >>> print(1/e) + 0.367879441171442 + + For nonrectangular areas, one can call :func:`~mpmath.quad` recursively. + For example, we can replicate the earlier example of calculating + `\pi` by integrating over the unit-circle, and actually use double + quadrature to actually measure the area circle:: + + >>> f = lambda x: quad(lambda y: 1, [-sqrt(1-x**2), sqrt(1-x**2)]) + >>> quad(f, [-1, 1]) + 3.14159265358979 + + Here is a simple triple integral:: + + >>> mp.dps = 15 + >>> f = lambda x,y,z: x*y/(1+z) + >>> quad(f, [0,1], [0,1], [1,2], method='gauss-legendre') + 0.101366277027041 + >>> (log(3)-log(2))/4 + 0.101366277027041 + + **Singularities** + + Both tanh-sinh and Gauss-Legendre quadrature are designed to + integrate smooth (infinitely differentiable) functions. Neither + algorithm copes well with mid-interval singularities (such as + mid-interval discontinuities in `f(x)` or `f'(x)`). + The best solution is to split the integral into parts:: + + >>> mp.dps = 15 + >>> quad(lambda x: abs(sin(x)), [0, 2*pi]) # Bad + 3.99900894176779 + >>> quad(lambda x: abs(sin(x)), [0, pi, 2*pi]) # Good + 4.0 + + The tanh-sinh rule often works well for integrands having a + singularity at one or both endpoints:: + + >>> mp.dps = 15 + >>> quad(log, [0, 1], method='tanh-sinh') # Good + -1.0 + >>> quad(log, [0, 1], method='gauss-legendre') # Bad + -0.999932197413801 + + However, the result may still be inaccurate for some functions:: + + >>> quad(lambda x: 1/sqrt(x), [0, 1], method='tanh-sinh') + 1.99999999946942 + + This problem is not due to the quadrature rule per se, but to + numerical amplification of errors in the nodes. The problem can be + circumvented by temporarily increasing the precision:: + + >>> mp.dps = 30 + >>> a = quad(lambda x: 1/sqrt(x), [0, 1], method='tanh-sinh') + >>> mp.dps = 15 + >>> +a + 2.0 + + **Highly variable functions** + + For functions that are smooth (in the sense of being infinitely + differentiable) but contain sharp mid-interval peaks or many + "bumps", :func:`~mpmath.quad` may fail to provide full accuracy. For + example, with default settings, :func:`~mpmath.quad` is able to integrate + `\sin(x)` accurately over an interval of length 100 but not over + length 1000:: + + >>> quad(sin, [0, 100]); 1-cos(100) # Good + 0.137681127712316 + 0.137681127712316 + >>> quad(sin, [0, 1000]); 1-cos(1000) # Bad + -37.8587612408485 + 0.437620923709297 + + One solution is to break the integration into 10 intervals of + length 100:: + + >>> quad(sin, linspace(0, 1000, 10)) # Good + 0.437620923709297 + + Another is to increase the degree of the quadrature:: + + >>> quad(sin, [0, 1000], maxdegree=10) # Also good + 0.437620923709297 + + Whether splitting the interval or increasing the degree is + more efficient differs from case to case. Another example is the + function `1/(1+x^2)`, which has a sharp peak centered around + `x = 0`:: + + >>> f = lambda x: 1/(1+x**2) + >>> quad(f, [-100, 100]) # Bad + 3.64804647105268 + >>> quad(f, [-100, 100], maxdegree=10) # Good + 3.12159332021646 + >>> quad(f, [-100, 0, 100]) # Also good + 3.12159332021646 + + **References** + + 1. http://mathworld.wolfram.com/DoubleIntegral.html + + """ + rule = kwargs.get('method', 'tanh-sinh') + if type(rule) is str: + if rule == 'tanh-sinh': + rule = ctx._tanh_sinh + elif rule == 'gauss-legendre': + rule = ctx._gauss_legendre + else: + raise ValueError("unknown quadrature rule: %s" % rule) + else: + rule = rule(ctx) + verbose = kwargs.get('verbose') + dim = len(points) + orig = prec = ctx.prec + epsilon = ctx.eps/8 + m = kwargs.get('maxdegree') or rule.guess_degree(prec) + points = [ctx._as_points(p) for p in points] + try: + ctx.prec += 20 + if dim == 1: + v, err = rule.summation(f, points[0], prec, epsilon, m, verbose) + elif dim == 2: + v, err = rule.summation(lambda x: \ + rule.summation(lambda y: f(x,y), \ + points[1], prec, epsilon, m)[0], + points[0], prec, epsilon, m, verbose) + elif dim == 3: + v, err = rule.summation(lambda x: \ + rule.summation(lambda y: \ + rule.summation(lambda z: f(x,y,z), \ + points[2], prec, epsilon, m)[0], + points[1], prec, epsilon, m)[0], + points[0], prec, epsilon, m, verbose) + else: + raise NotImplementedError("quadrature must have dim 1, 2 or 3") + finally: + ctx.prec = orig + if kwargs.get("error"): + return +v, err + return +v + + def quadts(ctx, *args, **kwargs): + """ + Performs tanh-sinh quadrature. The call + + quadts(func, *points, ...) + + is simply a shortcut for: + + quad(func, *points, ..., method=TanhSinh) + + For example, a single integral and a double integral: + + quadts(lambda x: exp(cos(x)), [0, 1]) + quadts(lambda x, y: exp(cos(x+y)), [0, 1], [0, 1]) + + See the documentation for quad for information about how points + arguments and keyword arguments are parsed. + + See documentation for TanhSinh for algorithmic information about + tanh-sinh quadrature. + """ + kwargs['method'] = 'tanh-sinh' + return ctx.quad(*args, **kwargs) + + def quadgl(ctx, *args, **kwargs): + """ + Performs Gauss-Legendre quadrature. The call + + quadgl(func, *points, ...) + + is simply a shortcut for: + + quad(func, *points, ..., method=GaussLegendre) + + For example, a single integral and a double integral: + + quadgl(lambda x: exp(cos(x)), [0, 1]) + quadgl(lambda x, y: exp(cos(x+y)), [0, 1], [0, 1]) + + See the documentation for quad for information about how points + arguments and keyword arguments are parsed. + + See documentation for TanhSinh for algorithmic information about + tanh-sinh quadrature. + """ + kwargs['method'] = 'gauss-legendre' + return ctx.quad(*args, **kwargs) + + def quadosc(ctx, f, interval, omega=None, period=None, zeros=None): + r""" + Calculates + + .. math :: + + I = \int_a^b f(x) dx + + where at least one of `a` and `b` is infinite and where + `f(x) = g(x) \cos(\omega x + \phi)` for some slowly + decreasing function `g(x)`. With proper input, :func:`~mpmath.quadosc` + can also handle oscillatory integrals where the oscillation + rate is different from a pure sine or cosine wave. + + In the standard case when `|a| < \infty, b = \infty`, + :func:`~mpmath.quadosc` works by evaluating the infinite series + + .. math :: + + I = \int_a^{x_1} f(x) dx + + \sum_{k=1}^{\infty} \int_{x_k}^{x_{k+1}} f(x) dx + + where `x_k` are consecutive zeros (alternatively + some other periodic reference point) of `f(x)`. + Accordingly, :func:`~mpmath.quadosc` requires information about the + zeros of `f(x)`. For a periodic function, you can specify + the zeros by either providing the angular frequency `\omega` + (*omega*) or the *period* `2 \pi/\omega`. In general, you can + specify the `n`-th zero by providing the *zeros* arguments. + Below is an example of each:: + + >>> from sympy.mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> f = lambda x: sin(3*x)/(x**2+1) + >>> quadosc(f, [0,inf], omega=3) + 0.37833007080198 + >>> quadosc(f, [0,inf], period=2*pi/3) + 0.37833007080198 + >>> quadosc(f, [0,inf], zeros=lambda n: pi*n/3) + 0.37833007080198 + >>> (ei(3)*exp(-3)-exp(3)*ei(-3))/2 # Computed by Mathematica + 0.37833007080198 + + Note that *zeros* was specified to multiply `n` by the + *half-period*, not the full period. In theory, it does not matter + whether each partial integral is done over a half period or a full + period. However, if done over half-periods, the infinite series + passed to :func:`~mpmath.nsum` becomes an *alternating series* and this + typically makes the extrapolation much more efficient. + + Here is an example of an integration over the entire real line, + and a half-infinite integration starting at `-\infty`:: + + >>> quadosc(lambda x: cos(x)/(1+x**2), [-inf, inf], omega=1) + 1.15572734979092 + >>> pi/e + 1.15572734979092 + >>> quadosc(lambda x: cos(x)/x**2, [-inf, -1], period=2*pi) + -0.0844109505595739 + >>> cos(1)+si(1)-pi/2 + -0.0844109505595738 + + Of course, the integrand may contain a complex exponential just as + well as a real sine or cosine:: + + >>> quadosc(lambda x: exp(3*j*x)/(1+x**2), [-inf,inf], omega=3) + (0.156410688228254 + 0.0j) + >>> pi/e**3 + 0.156410688228254 + >>> quadosc(lambda x: exp(3*j*x)/(2+x+x**2), [-inf,inf], omega=3) + (0.00317486988463794 - 0.0447701735209082j) + >>> 2*pi/sqrt(7)/exp(3*(j+sqrt(7))/2) + (0.00317486988463794 - 0.0447701735209082j) + + **Non-periodic functions** + + If `f(x) = g(x) h(x)` for some function `h(x)` that is not + strictly periodic, *omega* or *period* might not work, and it might + be necessary to use *zeros*. + + A notable exception can be made for Bessel functions which, though not + periodic, are "asymptotically periodic" in a sufficiently strong sense + that the sum extrapolation will work out:: + + >>> quadosc(j0, [0, inf], period=2*pi) + 1.0 + >>> quadosc(j1, [0, inf], period=2*pi) + 1.0 + + More properly, one should provide the exact Bessel function zeros:: + + >>> j0zero = lambda n: findroot(j0, pi*(n-0.25)) + >>> quadosc(j0, [0, inf], zeros=j0zero) + 1.0 + + For an example where *zeros* becomes necessary, consider the + complete Fresnel integrals + + .. math :: + + \int_0^{\infty} \cos x^2\,dx = \int_0^{\infty} \sin x^2\,dx + = \sqrt{\frac{\pi}{8}}. + + Although the integrands do not decrease in magnitude as + `x \to \infty`, the integrals are convergent since the oscillation + rate increases (causing consecutive periods to asymptotically + cancel out). These integrals are virtually impossible to calculate + to any kind of accuracy using standard quadrature rules. However, + if one provides the correct asymptotic distribution of zeros + (`x_n \sim \sqrt{n}`), :func:`~mpmath.quadosc` works:: + + >>> mp.dps = 30 + >>> f = lambda x: cos(x**2) + >>> quadosc(f, [0,inf], zeros=lambda n:sqrt(pi*n)) + 0.626657068657750125603941321203 + >>> f = lambda x: sin(x**2) + >>> quadosc(f, [0,inf], zeros=lambda n:sqrt(pi*n)) + 0.626657068657750125603941321203 + >>> sqrt(pi/8) + 0.626657068657750125603941321203 + + (Interestingly, these integrals can still be evaluated if one + places some other constant than `\pi` in the square root sign.) + + In general, if `f(x) \sim g(x) \cos(h(x))`, the zeros follow + the inverse-function distribution `h^{-1}(x)`:: + + >>> mp.dps = 15 + >>> f = lambda x: sin(exp(x)) + >>> quadosc(f, [1,inf], zeros=lambda n: log(n)) + -0.25024394235267 + >>> pi/2-si(e) + -0.250243942352671 + + **Non-alternating functions** + + If the integrand oscillates around a positive value, without + alternating signs, the extrapolation might fail. A simple trick + that sometimes works is to multiply or divide the frequency by 2:: + + >>> f = lambda x: 1/x**2+sin(x)/x**4 + >>> quadosc(f, [1,inf], omega=1) # Bad + 1.28642190869861 + >>> quadosc(f, [1,inf], omega=0.5) # Perfect + 1.28652953559617 + >>> 1+(cos(1)+ci(1)+sin(1))/6 + 1.28652953559617 + + **Fast decay** + + :func:`~mpmath.quadosc` is primarily useful for slowly decaying + integrands. If the integrand decreases exponentially or faster, + :func:`~mpmath.quad` will likely handle it without trouble (and generally be + much faster than :func:`~mpmath.quadosc`):: + + >>> quadosc(lambda x: cos(x)/exp(x), [0, inf], omega=1) + 0.5 + >>> quad(lambda x: cos(x)/exp(x), [0, inf]) + 0.5 + + """ + a, b = ctx._as_points(interval) + a = ctx.convert(a) + b = ctx.convert(b) + if [omega, period, zeros].count(None) != 2: + raise ValueError( \ + "must specify exactly one of omega, period, zeros") + if a == ctx.ninf and b == ctx.inf: + s1 = ctx.quadosc(f, [a, 0], omega=omega, zeros=zeros, period=period) + s2 = ctx.quadosc(f, [0, b], omega=omega, zeros=zeros, period=period) + return s1 + s2 + if a == ctx.ninf: + if zeros: + return ctx.quadosc(lambda x:f(-x), [-b,-a], lambda n: zeros(-n)) + else: + return ctx.quadosc(lambda x:f(-x), [-b,-a], omega=omega, period=period) + if b != ctx.inf: + raise ValueError("quadosc requires an infinite integration interval") + if not zeros: + if omega: + period = 2*ctx.pi/omega + zeros = lambda n: n*period/2 + #for n in range(1,10): + # p = zeros(n) + # if p > a: + # break + #if n >= 9: + # raise ValueError("zeros do not appear to be correctly indexed") + n = 1 + s = ctx.quadgl(f, [a, zeros(n)]) + def term(k): + return ctx.quadgl(f, [zeros(k), zeros(k+1)]) + s += ctx.nsum(term, [n, ctx.inf]) + return s + +if __name__ == '__main__': + import doctest + doctest.testmod() +
    + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/latest/_modules/sympy.html b/latest/_modules/sympy.html new file mode 100644 index 000000000000..334997145c1d --- /dev/null +++ b/latest/_modules/sympy.html @@ -0,0 +1,179 @@ + + + + + + + + + + sympy — SymPy 0.7.6.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    + +

    Source code for sympy

    +"""SymPy is a Python library for symbolic mathematics. It aims to become a
    +full-featured computer algebra system (CAS) while keeping the code as
    +simple as possible in order to be comprehensible and easily extensible.
    +SymPy is written entirely in Python and does not require any external
    +libraries, except optionally for plotting support.
    +
    +See the webpage for more information and documentation:
    +
    +    http://sympy.org
    +"""
    +
    +from __future__ import absolute_import, print_function
    +
    +from sympy.release import __version__
    +
    +import sys
    +if sys.version_info[0] == 2 and sys.version_info[1] < 6:
    +    raise ImportError("Python Version 2.6 or above is required for SymPy.")
    +else:  # Python 3
    +    pass
    +    # Here we can also check for specific Python 3 versions, if needed
    +
    +del sys
    +
    +
    +def __sympy_debug():
    +    # helper function so we don't import os globally
    +    import os
    +    debug_str = os.getenv('SYMPY_DEBUG', 'False')
    +    if debug_str in ('True', 'False'):
    +        return eval(debug_str)
    +    else:
    +        raise RuntimeError("unrecognized value for SYMPY_DEBUG: %s" %
    +                           debug_str)
    +SYMPY_DEBUG = __sympy_debug()
    +
    +from .core import *
    +from .logic import *
    +from .assumptions import *
    +from .polys import *
    +from .series import *
    +from .functions import *
    +from .ntheory import *
    +from .concrete import *
    +from .simplify import *
    +from .sets import *
    +from .solvers import *
    +from .matrices import *
    +from .geometry import *
    +from .utilities import *
    +from .integrals import *
    +from .tensor import *
    +from .parsing import *
    +from .calculus import *
    +# Adds about .04-.05 seconds of import time
    +# from combinatorics import *
    +# This module is slow to import:
    +#from physics import units
    +from .plotting import plot, textplot, plot_backends, plot_implicit
    +from .printing import pretty, pretty_print, pprint, pprint_use_unicode, \
    +    pprint_try_use_unicode, print_gtk, print_tree, pager_print, TableForm
    +from .printing import ccode, fcode, jscode, mathematica_code, octave_code, \
    +    latex, preview
    +from .printing import python, print_python, srepr, sstr, sstrrepr
    +from .interactive import init_session, init_printing
    +
    +evalf._create_evalf_table()
    +
    +# This is slow to import:
    +#import abc
    +
    + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/latest/_modules/sympy/galgebra/GA.html b/latest/_modules/sympy/galgebra/GA.html new file mode 100644 index 000000000000..9bdf201e7a12 --- /dev/null +++ b/latest/_modules/sympy/galgebra/GA.html @@ -0,0 +1,2217 @@ + + + + + + + + + + sympy.galgebra.ga — SymPy 0.7.6.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    + +

    Source code for sympy.galgebra.ga

    +# sympy/galgebra/ga.py
    +
    +"""
    +ga.py implements the symbolic geometric algebra of an n-dimensional
    +vector space with constant metric (future versions will allow for a
    +metric that is a function of coordinates) with an arbitrary set of
    +basis vectors (whether they are orthogonal or not depends on the
    +metric the use inputs).
    +
    +All the products of geometric algebra -
    +
    +    geometric
    +    outer (wedge)
    +    inner (dots)
    +    left contraction
    +    right contaction
    +
    +and the geometric derivative applied to all products from both sides.
    +
    +For more information on the details of geometric algebra please look
    +at the documentation for the module.
    +"""
    +
    +from __future__ import print_function
    +
    +from functools import reduce
    +from itertools import combinations
    +
    +import copy
    +import operator
    +
    +from sympy import Symbol, Expr, expand, Mul, Add, S, collect, \
    +    Function, simplify, diff, trigsimp, sqrt, Number, \
    +    factor_terms, sin, cos, sinh, cosh
    +from sympy import N as Nsympy
    +
    +from sympy.galgebra.printing import GA_Printer, GA_LatexPrinter, enhance_print, latex
    +from sympy.galgebra.vector import Vector
    +from sympy.galgebra.debug import oprint
    +from sympy.galgebra.stringarrays import fct_sym_array, str_combinations
    +from sympy.galgebra.ncutil import linear_expand, bilinear_product, nc_substitue, \
    +    get_commutative_coef, ONE_NC
    +
    +
    +
    [docs]def diagpq(p, q=0): + """ + Returns string equivalent metric tensor for signature (p, q). + """ + n = p + q + D = [] + for i in range(p): + D.append((i*'0 ' +'1 '+ (n-i-1)*'0 ')[:-1]) + for i in range(p,n): + D.append((i*'0 ' +'-1 '+ (n-i-1)*'0 ')[:-1]) + return ','.join(D) + +
    +
    [docs]def arbitrary_metric(n): + """ + Returns string equivalent metric tensor for arbitrary signature. + """ + return ','.join(n*[(n*'# ')[:-1]]) + +
    +
    [docs]def arbitrary_metric_conformal(n): + """ + Returns string equivalent metric tensor for arbitrary signature (n+1,1). + """ + str1 = ','.join(n*[n*'# '+'0 0']) + return ','.join([str1, n*'0 '+'1 0', n*'0 '+'0 -1']) + +
    +def make_coef(self, coef_str): + if self.fct: + if self.vars is not None: + return Function(coef_str)(*self.vars) + elif MV.coords is not None: + return Function(coef_str)(*MV.coords) + else: + return Symbol(coef_str) + else: + return Symbol(coef_str) + + +class MV(object): + """ + 'MV' class wraps sympy expressions of the form + + s = s_0 + s_1*b_1 + ... + s_K*b_K + + where the s_i are real sympy scalars (commutative expressions) and + the b_i are non-commutative sympy symbols. For an N-dimensional + vector space K = 2**N - 1. + + The linear combination of scalar (commutative) sympy quatities and the + basis multivectors form the multivector space. If the number of basis + vectors is 'n' the dimension of the multivector space is 2**n. If the + basis of the underlying vector space is (a_1,...,a_n) then the bases + of the multivector space are the noncommunicative geometric products + of the basis vectors of the form a_i1*a_i2*...*a_ir where i1 < i2 < ... < ir + (normal order) and the scalar 1. A multivector space is the vector + space with these bases over the sympy scalars. A basic assumption of + the geometric product, '*', is that it is associative and that the + geometric product of a vector with itself is a scalar. Thus we define + for any two vectors - + + a.b = (a*b + b*a)/2 [1] (D&L 4.7) + + noting then that a.a = a*a, a.b = b.a, and that a.b is a scalar. The + order of the geometric product of any two vectors can be reversed + with - + + b*a = 2*(a.b) - a*b [2] (D&L 4.30) + + This is all that is required to reduce the geometric product of any + number of basis vectors in any order to a linear combination of + normal order basis vectors. Note that a dot product for these bases + has not yet been defined and when it is the bases will not be orthogonal + unless the basis vectors are orthogonal. + + The outer product of two vectors is defined to be - + + a^b = (a*b - b*a)/2 [3] (D&L 4.8) + + This is generalized by the formula + + a^R_k = (a*R_k + (-1)**k*R_k*a)/2 [4] (D&L 4.38) + + where R_k is the outer product of k vectors (k-blade) and equation + [4] recursively defines the outer product of k + 1 vectors in terms of + the linear combination of geometric products of terms with k + 1 and + fewer vectors. + + D&L is "Geometric Algebra for Physicists" by Chris Doran and + Anthony Lasenby, Cambridge University Press. + """ + + ##########Methods for products (*,^,|) of orthogonal blades######### + """ + No multiplication tables (*,^,|) are calculated if the basis vectors + are orthogonal. All types of products are calculated on the fly and + the basis bases and blades are identical. + """ + + latex_flg = False + dot_mode = 's' # 's' - symmetric, 'l' - left contraction, 'r' - right contraction + + @staticmethod + def product_orthogonal_blades(blade1, blade2): + blade_index = list(MV.blade_to_index[blade1] + MV.blade_to_index[blade2]) + repeats = [] + sgn = 1 + for i in range(1, len(blade_index)): + save = blade_index[i] + j = i + while j > 0 and blade_index[j - 1] > save: + sgn = -sgn + blade_index[j] = blade_index[j - 1] + j -= 1 + blade_index[j] = save + if blade_index[j] == blade_index[j - 1]: + repeats.append(save) + result = S(sgn) + for i in repeats: + blade_index.remove(i) + blade_index.remove(i) + result *= MV.metric[i] + result *= MV.index_to_blade[tuple(blade_index)] + return result + + @staticmethod + def dot_orthogonal_blades(blade1, blade2): + + index1 = MV.blade_to_index[blade1] + index2 = MV.blade_to_index[blade2] + index = list(index1 + index2) + grade1 = len(index1) + grade2 = len(index2) + + if MV.dot_mode == 's': + if grade1 == 0: + return S.Zero + elif grade2 == 0: + return S.Zero + else: + grade = abs(grade1 - grade2) + elif MV.dot_mode == 'l': + grade = grade2 - grade1 + if grade < 0: + return S.Zero + if grade1 == 0: + return blade2 + elif MV.dot_mode == 'r': + grade = grade1 - grade2 + if grade < 0: + return S.Zero + if grade2 == 0: + return blade1 + n = len(index) + sgn = 1 + result = S.One + ordered = False + while n > grade: + ordered = True + i2 = 1 + while i2 < n: + i1 = i2 - 1 + index1 = index[i1] + index2 = index[i2] + if index1 == index2: + n -= 2 + if n < grade: + return S.Zero + result *= MV.metric[index1] + index = index[:i1] + index[i2 + 1:] + elif index1 > index2: + ordered = False + index[i1] = index2 + index[i2] = index1 + sgn = -sgn + i2 += 1 + else: + i2 += 1 + if ordered: + break + if n > grade: + return S.Zero + else: + return sgn * result * MV. index_to_blade[tuple(index)] + + ######################Multivector Constructors###################### + + def __init__(self, base=None, mvtype=None, fct=False, blade_rep=False): + + """ + Initialization of multivector X. Inputs are as follows + + mvtype base result + + default default Zero multivector + 'basisvector' int i ith basis vector + 'basisbivector' int i ith basis bivector + 'scalar' x scalar of value x + 's' + 'grade' [A] X.grade(i) = A + 's,i' + 'vector' [A] X.grade(1) = [A] + 's' + 'grade2' or 'bivector' [A] X.grade(2) = A + 's' + 'pseudo' x X.grade(n) = x + 's' + 'spinor' 's' spinor with coefficients + s__indices and name s + 'mv' 's' general multivector with + s__indices and name s + + If fct is 'True' and MV.coords is defined in MV.setup then a + multivector field of MV.coords is instantiated. + + Multivector data members are: + + obj - a sympy expression consisting of a linear + combination of sympy scalars and bases/blades. + + blade_rep - 'True' if 'MV' representation is a blade expansion, + 'False' if 'MV' representation is a base expansion. + """ + + def make_scalar(self, base): # make a scalar (grade 0) + if isinstance(base, str): + if self.fct: + self.obj = Function(base)(*MV.coords) * MV.ONE + else: + self.obj = make_coef(self, base) * MV.ONE + else: + self.obj = base * MV.ONE + self.igrade = 0 + self.blade_rep = True + return self + + def make_vector(self, base): # make a vector (grade 1) + if isinstance(base, str): + if self.fct: + base_lst = str_combinations(base, MV.coords, rank=1, mode='__') + fct_lst = fct_sym_array(base_lst, MV.coords) + self.obj = reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[1])))) + else: + if MV.coords is not None: + base_lst = str_combinations(base, MV.coords, rank=1, mode='__') + else: + base_lst = str_combinations(base, MV.subscripts, rank=1, mode='__') + fct_lst = fct_sym_array(base_lst, None) + self.obj = reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[1])))) + else: + result = S.Zero + for (coef, base) in zip(base, MV.blades[1]): + result += coef * base + self.obj = result + self.igrade = 1 + self.blade_rep = True + return self + + def make_basisvector(self, base): + raise NotImplementedError("Don't know how to compute basis vectors of class %" % self.__class__) + + def make_basisbivector(self, base): + raise NotImplementedError("Don't know how to compute basis bivectors of class %" % self.__class__) + + def make_grade(self, base): # if base is 'A,n' then make a grade n multivector + if isinstance(base, str): + base_lst = base.split(',') + base = base_lst[0] + n = int(base_lst[1]) + if self.fct: + base_lst = str_combinations(base, MV.coords, rank=n, mode='__') + fct_lst = fct_sym_array(base_lst, MV.coords) + self.obj = reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[n])))) + else: + if MV.coords is not None: + base_lst = str_combinations(base, MV.coords, rank=n, mode='__') + else: + base_lst = str_combinations(base, MV.subscripts, rank=n, mode='__') + fct_lst = fct_sym_array(base_lst, None) + self.obj = reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[n])))) + else: + raise TypeError('Cannot make_grade for base = %s' % base) + self.igrade = n + self.blade_rep = True + return self + + def make_grade2(self, base): # grade 2 multivector + if isinstance(base, str): + if self.fct: + base_lst = str_combinations(base, MV.coords, rank=2, mode='__') + fct_lst = fct_sym_array(base_lst, MV.coords) + self.obj = reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[2])))) + else: + if MV.coords is not None: + base_lst = str_combinations(base, MV.coords, rank=2, mode='__') + else: + base_lst = str_combinations(base, MV.subscripts, rank=2, mode='__') + fct_lst = fct_sym_array(base_lst, None) + self.obj = reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[2])))) + else: + raise TypeError('!!!!Cannot make_grade2 for base = ' + str(base) + '!!!!\n') + self.igrade = 2 + self.blade_rep = True + return self + + def make_pseudo(self, base): # multivector of grade MV.dim + if isinstance(base, str): + if self.fct: + base_lst = str_combinations(base, MV.coords, rank=MV.dim, mode='__') + fct_lst = fct_sym_array(base_lst, MV.coords) + self.obj = reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[MV.dim])))) + else: + if MV.coords is not None: + base_lst = str_combinations(base, MV.coords, rank=MV.dim, mode='__') + else: + base_lst = str_combinations(base, MV.subscripts, rank=MV.dim, mode='__') + fct_lst = fct_sym_array(base_lst, None) + self.obj = reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[MV.dim])))) + else: + raise TypeError('!!!!Cannot make_pseudo for base = ' + str(base) + '!!!!\n') + self.igrade = MV.dim + self.blade_rep = True + return self + + def make_spinor(self, base): # multivector with all even grades + if isinstance(base, str): + if self.fct: + self.obj = Function(base)(*MV.coords) * MV.ONE + else: + self.obj = Symbol(base) * MV.ONE + for rank in range(2, MV.dim1, 2): + if self.fct: + base_lst = str_combinations(base, MV.coords, rank=rank, mode='__') + fct_lst = fct_sym_array(base_lst, MV.coords) + self.obj += reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[rank])))) + else: + if MV.coords is not None: + base_lst = str_combinations(base, MV.coords, rank=rank, mode='__') + else: + base_lst = str_combinations(base, MV.subscripts, rank=rank, mode='__') + fct_lst = fct_sym_array(base_lst, None) + self.obj += reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[rank])))) + else: + raise TypeError('Cannot make_mv for base = %s' % base) + self.igrade = -1 + self.blade_rep = True + return self + + def make_mv(self, base): + if isinstance(base, str): + if self.fct: + self.obj = Function(base)(*MV.coords) * MV.ONE + else: + self.obj = Symbol(base) * MV.ONE + for rank in range(1, MV.dim1): + if self.fct: + base_lst = str_combinations(base, MV.coords, rank=rank, mode='__') + fct_lst = fct_sym_array(base_lst, MV.coords) + self.obj += reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[rank])))) + else: + if MV.coords is not None: + base_lst = str_combinations(base, MV.coords, rank=rank, mode='__') + else: + base_lst = str_combinations(base, MV.subscripts, rank=rank, mode='__') + fct_lst = fct_sym_array(base_lst, None) + self.obj += reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[rank])))) + else: + raise TypeError('!!!!Cannot make_mv for base = ' + str(base) + '!!!!\n') + self.igrade = -1 + self.blade_rep = True + return self + + MVtypes = {'scalar': make_scalar, + 'vector': make_vector, + 'basisvector': make_basisvector, + 'basisbivector': make_basisbivector, + 'grade': make_grade, + 'grade2': make_grade2, + 'bivector': make_grade2, + 'pseudo': make_pseudo, + 'spinor': make_spinor, + 'mv': make_mv} + + self.fct = fct + self.is_base = False + self.is_grad = False + self.print_blades = MV.print_blades + self.fmt = 1 + + if mvtype is None: + if base in (None, S.Zero): # Default is zero multivector + self.blade_rep = True + self.obj = S.Zero + self.igrade = 0 + elif isinstance(base, str): # Base or blade basis multivector + self.is_base = True + if '*' in base: + self.blade_rep = False + self.igrade = -1 + else: + if '^' in base: + self.blade_rep = True + self.igrade = base.count('^') + 1 + else: + self.blade_rep = blade_rep + self.igrade = 1 + self.obj = Symbol(base, commutative=False) + elif isinstance(base, MV): # Copy constructor + self.blade_rep = base.blade_rep + self.obj = base.obj + self.igrade = base.igrade + self.fct = base.fct + self.is_base = base.is_base + self.is_grad = base.is_grad + elif isinstance(base, (Expr, Symbol)): # Gets properties of multivector from Expr + if base.is_commutative: + self.obj = base * MV.ONE + self.blade_rep = True + self.igrade = 0 + else: + if isinstance(base, (Add, Mul)): # Complex expression + self = MV.characterize_expression(self, base) + elif isinstance(base, Symbol): + if not base.is_commutative: + if base == MV.ONE: + self.obj = base + self.blade_rep = True + self.igrade = 0 + elif base in MV.blades_flat: # basis blade + self.obj = base + self.blade_rep = True + self.igrade = MV.blade_grades[base] + elif base in MV.bases_flat: # basis base + self.obj = base + self.blade_rep = False + self.igrade = -1 + else: + raise ValueError('MV(' + str(base) + ') is not allowed in constructor\n' + + 'non-commutative argument is not a base\n') + else: # scalar sympy symbol + self.obj = base * MV.ONE + self.igrade = 0 + self.blade_rep = True + elif isinstance(base, Number): + self.obj = base * MV.ONE + self.igrade = 0 + self.blade_rep = True + else: # Preconfigured multivector types + self = MVtypes[mvtype](self, base) + + def Fmt(self, fmt=1, title=None): + self.fmt = fmt + if title is not None: + print(title + ' = ' + str(self)) + return + return self + + def __str__(self): + if GA_LatexPrinter.latex_flg: + Printer = GA_LatexPrinter + else: + Printer = GA_Printer + self.discover_and_set_grade() + self.obj = expand(self.obj).collect(MV.blades_flat) + return Printer().doprint(self) + + ######################## Operator Definitions####################### + + def coef(self, base): + (coefs, bases) = linear_expand(self.obj) + if base.obj in bases: + icoef = bases.index(base.obj) + return coefs[icoef] + else: + return S.Zero + + def func(self, fct): + (coefs, bases) = linear_expand(self.obj) + result = S.Zero + for (coef, base) in zip(coefs, bases): + result += fct(coef) * base + fself = MV(self) + fself.obj = result + return fself + + def __eq__(self, mv): + if not isinstance(mv, MV): + mv = MV(mv) + if self.obj == mv.obj: + return True + else: + return False + + def __neg__(self): # -self + nself = MV(self) + nself.obj = -self.obj + return nself + + def __pos__(self): # +self + return self + + def __add__(self, b): # self + b + if isinstance(b, MV): + self.base_to_blade() + b.base_to_blade() + self_add_b = MV.basic_add(self, b) + self_add_b.discover_and_set_grade() + else: + self_add_b = MV(self) + self_add_b.obj = self.obj + b * MV.ONE + if self.igrade != 0: + self_add_b.igrade = -1 + return self_add_b + + def __radd__(self, b): # b + self + b_add_self = MV(self) + b_add_self.obj = b * MV.ONE + self.obj + if self.igrade != 0: + b_add_self.igrade = -1 + return b_add_self + + def __add_ab__(self, b): # self += b + selfb = MV() + selfb.obj += b.obj + return selfb + + def __sub__(self, b): # self - b + if isinstance(b, MV): + self.base_to_blade() + b.base_to_blade() + self_sub_b = MV.basic_sub(self, b) + self_sub_b.discover_and_set_grade() + else: + self_sub_b = MV(self) + self_sub_b.obj = self.obj - b * MV.ONE + if self.igrade != 0: + self_sub_b.igrade = -1 + return self_sub_b + + def __sub_ab__(self, b): # self -= b + selfb = MV() + selfb.obj -= b.obj + return selfb + + def __rsub__(self, b): # b - self + b_sub_self = MV(self) + b_sub_self.obj = b * MV.ONE - self.obj + if self.igrade != 0: + b_sub_self.igrade = -1 + return b_sub_self + + def __mul__(self, b): # self*b + if isinstance(b, MV): + if self.is_grad: # left geometric derivative + result = MV() + if self.connection: + for (coord, brecp, bnorm) in zip(self.coords, self.rcpr_bases_MV, self.tangent_norm): + result += (brecp * b.diff(coord)) / bnorm + if b.igrade > 0: + result.obj += nc_substitue(b.obj, self.connection['left']) + else: + for (coord, brecp) in zip(self.coords, self.rcpr_bases_MV): + result += brecp * b.diff(coord) + return result + elif b.is_grad: # right geometric derivative + result = MV() + if b.connection: + for (coord, brecp, bnorm) in zip(b.coords, b.rcpr_bases_MV, b.tangent_norm): + result += (self.diff(coord) * brecp) / bnorm + if self.igrade > 0: + result.obj += nc_substitue(self.obj, b.connection['right']) + else: + for (coord, brecp) in zip(b.coords, b.rcpr_bases_MV): + result += self.diff(coord) * brecp + return MV(result) + else: + if not MV.is_orthogonal: + self.blade_to_base() + b.blade_to_base() + obj = bilinear_product(self.obj * b.obj, MV.geometric_product) + self_mul_b = MV(obj) + if not MV.is_orthogonal: + self_mul_b.igrade = -1 + self_mul_b.blade_rep = False + self_mul_b.base_to_blade() + self_mul_b.discover_and_set_grade() + return self_mul_b + else: + if self.is_grad: + result = MV() + for (coord, brecp) in zip(self.coords, self.rcpr_bases_MV): + result += brecp * diff(b, coord) + return result + else: + self_mul_b = MV(self) + self_mul_b.obj *= b + return self_mul_b + + def __mul_ab__(self, b): # self *= b + selfb = MV(self) + selfb.obj *= b.obj + return selfb + + def __rmul__(self, b): # b * self + b_mul_self = MV(self) + b_mul_self.obj = b * self.obj + return b_mul_self + + def __div__(self, b): # self / b + if not isinstance(b, MV): + self_div_b = MV(self) + self_div_b.obj = self.obj / b + return self_div_b + else: + raise TypeError('No multivector division for divisor = ' + str(b) + '\n') + + __truediv__ = __div__ + + def __or__(self, b): # self | b + if isinstance(b, MV): + if self.is_grad: # left dot/div (inner) derivative + result = MV() + if b.igrade == 0: + return result + if self.connection: + for (coord, brecp, bnorm) in zip(self.coords, self.rcpr_bases_MV, self.tangent_norm): + result += (brecp | b.diff(coord)) / bnorm + result.obj += nc_substitue(b.obj, self.connection['left_dot']) + else: + for (coord, brecp) in zip(self.coords, self.rcpr_bases_MV): + result += brecp | b.diff(coord) + return result + elif b.is_grad: # right dot/div (inner) derivative + result = MV() + if b.connection: + for (coord, brecp, bnorm) in zip(b.coords, b.rcpr_bases_MV, b.tangent_norm): + result += (self.diff(coord) | brecp) / bnorm + result.obj += nc_substitue(self.obj, b.connection['right_dot']) + else: + for (coord, brecp) in zip(b.coords, b.rcpr_bases_MV): + result += self.diff(coord) | brecp + return MV(result) + else: + if MV.is_orthogonal: + MV.dot_mode = 's' + result = bilinear_product(self.obj * b.obj, MV.dot_orthogonal_blades) + return MV(result) + else: + return MV.non_orthogonal_products(self, b, mode='s') + else: # dot product returns zero for r.h.s. scalar multiplier + return MV() + + def __ror__(self, b): # b | self + b_dot_self = MV() + return b_dot_self + + def __lt__(self, b): # left contraction + if isinstance(b, MV): + if self.is_grad: # left derivative for left contraction + result = MV() + if self.connection: + for (coord, brecp, bnorm) in zip(self.coords, self.rcpr_bases_MV, self.tangent_norm): + result += (brecp < b.diff(coord)) / bnorm + result.obj += nc_substitue(b.obj, self.connection['left_dot']) + else: + for (coord, brecp) in zip(self.coords, self.rcpr_bases_MV): + result += brecp < b.diff(coord) + return result + elif b.is_grad: # right derivative for left contraction + result = MV() + if b.connection: + for (coord, brecp, bnorm) in zip(b.coords, b.rcpr_bases_MV, b.tangent_norm): + result += (self.diff(coord) < brecp) / bnorm + result.obj += nc_substitue(self.obj, b.connection['right_dot']) + else: + for (coord, brecp) in zip(b.coords, b.rcpr_bases_MV): + result += self.diff(coord) < brecp + return MV(result) + else: + if MV.is_orthogonal: + MV.dot_mode = 'l' + result = bilinear_product(self.obj * b.obj, MV.dot_orthogonal_blades) + return MV(result) + else: + return MV.non_orthogonal_products(self, b, mode='l') + + def __gt__(self, b): # right contraction + if isinstance(b, MV): + if self.is_grad: # left derivative for right contraction + result = MV() + if self.connection: + for (coord, brecp, bnorm) in zip(self.coords, self.rcpr_bases_MV, self.tangent_norm): + result += (brecp > b.diff(coord)) / bnorm + result.obj += nc_substitue(b.obj, self.connection['left_dot']) + else: + for (coord, brecp) in zip(self.coords, self.rcpr_bases_MV): + result += brecp > b.diff(coord) + return result + elif b.is_grad: # right derivative for right contraction + result = MV() + if b.connection: + for (coord, brecp, bnorm) in zip(b.coords, b.rcpr_bases_MV, b.tangent_norm): + result += (self.diff(coord) > brecp) / bnorm + result.obj += nc_substitue(self.obj, b.connection['right_dot']) + else: + for (coord, brecp) in zip(b.coords, b.rcpr_bases_MV): + result += self.diff(coord) > brecp + return MV(result) + else: + if MV.is_orthogonal: + MV.dot_mode = 'r' + result = bilinear_product(self.obj * b.obj, MV.dot_orthogonal_blades) + return MV(result) + else: + return MV.non_orthogonal_products(self, b, mode='r') + + def __xor__(self, b): # self ^ b + if isinstance(b, MV): + if self.is_grad: # left wedge/curl (outer) derivative + result = MV() + if self.connection: + for (coord, brecp, bnorm) in zip(self.coords, self.rcpr_bases_MV, self.tangent_norm): + result += (brecp ^ b.diff(coord)) / bnorm + result.obj += nc_substitue(b.obj, self.connection['left_wedge']) + else: + for (coord, brecp) in zip(self.coords, self.rcpr_bases_MV): + result += brecp ^ b.diff(coord) + return result + elif b.is_grad: # right wedge/curl (outer) derivative + result = MV() + if b.connection: + for (coord, brecp, bnorm) in zip(b.coords, b.rcpr_bases_MV, b.tangent_norm): + result += (self.diff(coord) ^ brecp) / bnorm + result.obj += nc_substitue(self.obj, b.connection['right_wedge']) + else: + for (coord, brecp) in zip(b.coords, b.rcpr_bases_MV): + result += self.diff(coord) ^ brecp + return MV(result) + else: + if MV.is_orthogonal: + result = bilinear_product(self.obj * b.obj, MV.wedge_product) + return MV(result) + else: + return MV.non_orthogonal_products(self, b, mode='w') + else: + if self.is_grad: + result = MV() + for (coord, brecp) in zip(self.coords, self.rcpr_bases_MV): + result += brecp * diff(b, coord) + return result + else: + return self * b + + def __rxor__(self, b): # b ^ self + b_W_self = MV(self) + b_W_self.obj = b * self.obj + return b_W_self + + def scalar(self): + (coefs, blades) = linear_expand(self.obj) + result = S.Zero + for (coef, blade) in zip(coefs, blades): + if MV.blade_grades[blade] == 0: + result += coef + return result + + def set_coef(self, igrade, ibase, value): + if self.blade_rep: + base = MV.blades[igrade][ibase] + else: + base = MV.bases[igrade][ibase] + (coefs, bases) = linear_expand(self.obj) + bases_lst = list(bases) # python 2.5 + if base in bases: + self.obj += (value - coefs[bases_lst.index(base)]) * base + else: + self.obj += value * base + return + + def grade(self, igrade=0): + if igrade > MV.dim: + return MV() + if self.igrade > -1: + if self.igrade == igrade: + return self + else: + return MV() + else: + (coefs, blades) = linear_expand(self.obj) + result = S.Zero + for (coef, blade) in zip(coefs, blades): + if MV.blade_grades[blade] == igrade: + result += coef * blade + self_igrade = MV(result) + self_igrade.igrade = igrade + return self_igrade + + def get_grades(self): # grade decomposition of multivector + self.base_to_blade() + (coefs, bases) = linear_expand(self.obj) + grades = {} + for (coef, base) in zip(coefs, bases): + igrade = MV.blade_grades[base] + if igrade in grades: + grades[igrade] += coef * base + else: + grades[igrade] = coef * base + for key in grades: # convert sympy expression to multivector + grade = MV(grades[key]) + grade.blad_rep = True + grade.igrade = key + grades[key] = grade + return grades + + def discover_and_set_grade(self): + self.base_to_blade() + (coefs, bases) = linear_expand(self.obj) + old_grade = -1 + first_flg = True + for (coef, base) in zip(coefs, bases): + igrade = MV.blade_grades[base] + if igrade != old_grade and first_flg: + first_flg = False + old_grade = igrade + elif igrade != old_grade: + self.igrade = -1 + return + self.igrade = old_grade + return + + def get_normal_order_str(self): + self.obj = expand(self.obj) + if self.blade_rep: + self.obj = self.obj.collect(MV.blades_flat1) + else: + self.obj = self.obj.collect(MV.bases_flat1) + terms = zip(*linear_expand(self.obj)) + if self.blade_rep: + terms = sorted(terms, key=lambda x: MV.blades_flat1_lst.index(x[1])) # Python 2.5 + else: + terms = sorted(terms, key=lambda x: MV.bases_flat1_lst.index(x[1])) # Python 2.5 + o_str = '' + first = True + if self.fmt == 2: + if terms[0][1] == MV.ONE: + grade = 0 + else: + s = str(factor_terms(terms[0][0])) + grade = max(s.count('^'), s.count('*')) + 1 + for term in terms: + if term[1] == MV.ONE: + tmp = str(factor_terms(term[0])) + else: + if isinstance(term[0], Add): + tmp = str(factor_terms(term[0])) + tmp = '(' + tmp + ')*' + enhance_print.enhance_base(str(term[1])) + else: + coef_str = str(factor_terms(term[0])) + if coef_str == '1': + coef_str = '' + elif coef_str == '-1': + coef_str = '-' + else: + coef_str += '*' + tmp = coef_str + enhance_print.enhance_base(str(term[1])) + if first: + first = False + o_str += tmp + else: + nl = '' + if self.fmt == 2: + s = str(term[1]) + new_grade = max(s.count('^'), s.count('*')) + 1 + if new_grade > grade: + nl = '\n' + grade = new_grade + if tmp[0] == '-': + o_str += nl + ' - ' + tmp[1:] + else: + o_str += nl + ' + ' + tmp + if self.fmt == 3: + o_str += '\n' + + if o_str[-1] == '\n': + o_str = o_str[:-1] + return o_str + + def get_latex_normal_order_str(self): + + latex_sep = {'^': r'\W ', '*': ' '} + + def base_string(base_obj): + base_str = GA_LatexPrinter.Basic__str__(base_obj) + sep = '^' + if '*' in base_str: + sep = '*' + base_lst = base_str.split(sep) + + lstr = r'\bm{' + latex(Symbol(base_lst[0])) + for base in base_lst[1:]: + lstr += latex_sep[sep] + latex(Symbol(base)) + lstr += '}' + return lstr + + self.obj = expand(self.obj) + if self.blade_rep: + self.obj = self.obj.collect(MV.blades_flat) + else: + self.obj = self.obj.collect(MV.bases_flat) + terms = zip(*linear_expand(self.obj)) + if self.blade_rep: + bgrades = MV.blade_grades + terms = sorted(terms, key=lambda x: MV.blades_flat1_lst.index(x[1])) # Python 2.5 + else: + bgrades = MV.base_grades + terms = sorted(terms, key=lambda x: MV.bases_flat1_lst.index(x[1])) # Python 2.5 + grades = [] + bases = [] + old_grade = -1 + for term in terms: + new_grade = bgrades[term[1]] + if old_grade != new_grade: + if old_grade > -1: + grades.append(bases) + bases = [] + old_grade = new_grade + bases.append(term) + if len(bases) > 0: + grades.append(bases) + + o_str = '' + grade_strs = [] + nbases = 0 + for grade in grades: # create [grade[base]] list of base strings + base_strs = [] + for base in grade: + nbases += 1 + if base[1] == MV.ONE: + base_str = latex(simplify(base[0])) + else: + if isinstance(base[0], Add): + base_str = r'\left ( ' + latex(simplify(base[0])) + r'\right ) ' + base_string(base[1]) + else: + coef_str = latex(simplify(base[0])) + if coef_str == '1': + coef_str = '' + elif coef_str == '-1': + coef_str = '-' + base_str = coef_str + base_string(base[1]) + if base_str[0] != '-': + base_str = '+' + base_str + base_strs.append(base_str) + grade_strs.append(base_strs) + if grade_strs[0][0][0] == '+': + grade_strs[0][0] = grade_strs[0][0][1:] + + o_str = '' + ngrades = len(grade_strs) + + if (self.fmt == 2 and ngrades > 1) or (self.fmt == 3 and nbases > 1): + o_str += '\\begin{align*} ' + + for base_strs in grade_strs: + if self.fmt == 2 and ngrades > 1: + o_str += ' & ' + for base_str in base_strs: + if self.fmt == 3 and nbases > 1: + o_str += ' & ' + base_str + ' \\\\ ' + else: + o_str += base_str + if self.fmt == 2 and ngrades > 1: + o_str += ' \\\\ ' + + if (self.fmt == 2 and ngrades > 1) or (self.fmt == 3 and nbases > 1): + o_str += '\\end{align*} \n' + else: + pass + return o_str + + @staticmethod + def characterize_expression(self, expr): + (coefs, bases) = linear_expand(expr) + self.obj = expr + if not MV.is_orthogonal: + if len(set(bases) & MV.bases_set) != 0: + self.blade_rep = False + self.igrade = -1 + else: + self.blade_rep = True + self.igrade = 0 + return self + else: + self.blade_rep = True + self.igrade = -1 + + if self.blade_rep: + self.igrade = MV.blade_grades[bases[0]] + for base in bases[1:]: + igrade = MV.blade_grades[base] + if self.igrade != igrade: + self.igrade = -1 + break + return self + return self + + def db(self): + print('(blade_rep,igrade,obj) =', self.blade_rep, self.igrade, self.obj) + return + + ##########################Member Functions########################## + + def dd(self, v): + (coefs, bases) = linear_expand(v.obj) + dderiv = MV() + for (coef, base) in zip(coefs, bases): + dderiv += coef * self.diff(MV.dd_dict[base]) + return dderiv + + def diff(self, var): + dself = MV(self) + dself.obj = diff(self.obj, var) + return dself + + def simplify(self): + (coefs, bases) = linear_expand(self.obj) + obj = 0 + for (coef, base) in zip(coefs, bases): + coef = simplify(coef) + obj += coef * base + sself = MV(self) + sself.obj = obj + return sself + + def trigsimp(self, **kwargs): + (coefs, bases) = linear_expand(self.obj) + obj = 0 + for (coef, base) in zip(coefs, bases): + coef = trigsimp(coef, **kwargs) + obj += coef * base + ts_self = MV(self) + ts_self.obj = obj + return ts_self + + def exp(self, alpha=1, norm=0, mode='T'): + if self.is_blade(): + self_sq = (self * self).scalar() + if mode == 'T': + if norm == 0: + norm = sqrt(-self_sq) + return cos(alpha * norm) + sin(alpha * norm) * self / norm + else: + if norm == 0: + norm = sqrt(self_sq) + return cosh(alpha * norm) + sinh(alpha * norm) * self / norm + else: + raise TypeError('!!! ' + str(self) + ' is not a blade in member function "exp" !!!\n') + + def expand(self): + xself = MV(self) + xself.obj = expand(self.obj) + return xself + + def factor(self): + fself = MV(self) + fself.obj = factor_terms(self.obj) + return fself + + def subs(self, x): + xsubs = self.obj.subs(x) + return MV(xsubs) + + def collect(self, x): + (coefs, bases) = linear_expand(self.obj) + result = S.Zero + for (coef, base) in zip(coefs, bases): + result += collect(coef, x) * base + return MV(result) + + def is_scalar(self): + self.discover_and_set_grade() + if self.igrade == 0: + return True + else: + return False + + def is_blade(self): + self_sq = self * self + if self_sq.is_scalar(): + return True + return False + + def dual(self): + dself = MV.I * self + dself.discover_and_set_grade() + return dself + + def even(self): + if self.igrade > -1: + if self.igrade % 2 == 0: + return self + else: + return MV() + else: + (coefs, blades) = linear_expand(self.obj) + result = S.Zero + for (coef, blade) in zip(coefs, blades): + if MV.blade_grades[blade] % 2 == 0: + result += coef * blade + return MV(result) + + def odd(self): + if self.igrade > -1: + if self.igrade % 2 == 1: + return self + else: + return MV() + else: + (coefs, blades) = linear_expand(self.obj) + result = S.Zero + for (coef, blade) in zip(coefs, blades): + if MV.blade_grades[blade] % 2 == 1: + result += coef * blade + return MV(result) + + def norm(self): + norm_sq = self * self.rev() + norm_sq.discover_and_set_grade() + if norm_sq.igrade == 0: + norm_sq = norm_sq.scalar() + if isinstance(norm_sq, Number): + norm = sqrt(abs(norm_sq)) + else: + norm = sqrt(abs(norm_sq)) + return norm + else: + raise ValueError('In norm self*self.rev() = ' + str(norm_sq) + + ' is not a scalar!\n') + + def norm2(self): + norm_sq = self * self.rev() + norm_sq.discover_and_set_grade() + if norm_sq.igrade == 0: + norm_sq = norm_sq.scalar() + return norm_sq + else: + raise ValueError('In norm self*self.rev() = ' + str(norm_sq) + + ' is not a scalar!\n') + + def rev(self): + self.base_to_blade() + (coefs, bases) = linear_expand(self.obj) + result = S.Zero + for (coef, base) in zip(coefs, bases): + grade = MV.blade_grades[base] + if grade < 2: + result += coef * base + else: + sgn_pow = (grade * (grade - 1)) / 2 % 2 + if sgn_pow == 1: + result -= coef * base + else: + result += coef * base + self_rev = MV() + self_rev.obj = result + self_rev.igrade = self.igrade + self_rev.blade_rep = self.blade_rep + self_rev.fct = self.fct + self_rev.is_grad = self.is_grad + self_rev.print_blades = MV.print_blades + self_rev.obj = simplify(self_rev.obj) + return self_rev + + def inv(self): + self_rev = self.rev() + norm = self * self_rev + norm.obj = expand(norm.obj) + norm.discover_and_set_grade() + if norm.igrade == 0: + return self_rev / norm.obj + else: + raise ValueError('Cannot take inv(A) since A*rev(A) = ' + str(norm) + + ' is not a scalar.\n') + + #######################Reduce Combined Indexes###################### + + @staticmethod + def reduce_basis_loop(blst): + """ + blst is a list of integers [i_{1},...,i_{r}] representing the geometric + product of r basis vectors a_{{i_1}}*...*a_{{i_r}}. reduce_basis_loop + searches along the list [i_{1},...,i_{r}] untill it finds i_{j} == i_{j+1} + and in this case contracts the list, or if i_{j} > i_{j+1} it revises + the list (~i_{j} means remove i_{j} from the list) + + Case 1: If i_{j} == i_{j+1}, return a_{i_{j}}**2 and + [i_{1},..,~i_{j},~i_{j+1},...,i_{r}] + + Case 2: If i_{j} > i_{j+1}, return a_{i_{j}}.a_{i_{j+1}}, + [i_{1},..,~i_{j},~i_{j+1},...,i_{r}], and + [i_{1},..,i_{j+1},i_{j},...,i_{r}] + """ + nblst = len(blst) # number of basis vectors + if nblst <= 1: + return True # a scalar or vector is already reduced + jstep = 1 + while jstep < nblst: + istep = jstep - 1 + if blst[istep] == blst[jstep]: # basis vectorindex is repeated + i = blst[istep] # save basis vector index + if len(blst) > 2: + blst = blst[:istep] + blst[jstep + 1:] # contract blst + else: + blst = [] + if len(blst) <= 1 or jstep == nblst - 1: + blst_flg = True # revision of blst is complete + else: + blst_flg = False # more revision needed + return MV.metric[i, i], blst, blst_flg + if blst[istep] > blst[jstep]: # blst not in normal order + blst1 = blst[:istep] + blst[jstep + 1:] # contract blst + a1 = MV.metric2[blst[jstep], blst[istep]] # coef of contraction + blst = blst[:istep] + [blst[jstep]] + [blst[istep]] + blst[jstep + 1:] # revise blst + if len(blst1) <= 1: + blst1_flg = True # revision of blst is complete + else: + blst1_flg = False # more revision needed + return a1, blst1, blst1_flg, blst + jstep += 1 + return True # revision complete, blst in normal order + + @staticmethod + def reduce_basis(blst): + """ + Repetitively applies reduce_basis_loop to blst + product representation until normal form is realized. + """ + if blst == []: # blst represents scalar + blst_coef = [S.One] + blst_expand = [[]] + return blst_coef, blst_expand + blst_expand = [blst] + blst_coef = [S.One] + blst_flg = [False] + # reduce untill all blst revise flgs are True + while not reduce(operator.and_, blst_flg): + for i in range(len(blst_flg)): + if not blst_flg[i]: # keep revising if revise flg is False + tmp = MV.reduce_basis_loop(blst_expand[i]) + if isinstance(tmp, bool): + blst_flg[i] = tmp # revision of blst_expand[i] complete + elif len(tmp) == 3: # blst_expand[i] contracted + blst_coef[i] = tmp[0] * blst_coef[i] + blst_expand[i] = tmp[1] + blst_flg[i] = tmp[2] + else: # blst_expand[i] revised + blst_coef[i] = -blst_coef[i] + # if revision force one more pass in case revision + # causes repeated index previous to revised pair of + # indexes + blst_flg[i] = False + blst_expand[i] = tmp[3] + blst_coef.append(-blst_coef[i] * tmp[0]) + blst_expand.append(tmp[1]) + blst_flg.append(tmp[2]) + new_blst_coef = [] + new_blst_expand = [] + for (coef, expand) in zip(blst_coef, blst_expand): + if expand in new_blst_expand: + i = new_blst_expand.index(expand) + new_blst_coef[i] += coef + else: + new_blst_expand.append(expand) + new_blst_coef.append(coef) + return new_blst_coef, new_blst_expand + + ##########################Bases Construction######################## + + @staticmethod + def symbol_product_bases(i1, i2): + if i1 == (): + if i2 == (): + return S.One + else: + return MV.index_to_base[i2] + else: + if i2 == (): + return MV.index_to_base[i1] + + index = list(i1 + i2) + result = S.Zero + (coefs, indexes) = MV.reduce_basis(index) + for (coef, index) in zip(coefs, indexes): + result += coef * MV.index_to_base[tuple(index)] + return result + + @staticmethod + def make_base_blade_symbol(ibase): + if len(ibase) == 1: + base_str = MV.basis_names[ibase[0]] + return Symbol(base_str, commutative=False), base_str, \ + Symbol(base_str, commutative=False), base_str + else: + base_str = '' + blade_str = '' + for index in ibase: + vector_str = MV.basis_names[index] + base_str += vector_str + '*' + blade_str += vector_str + '^' + base_str = base_str[:-1] + blade_str = blade_str[:-1] + return Symbol(base_str, commutative=False), base_str, \ + Symbol(blade_str, commutative=False), blade_str + + ################Geometric, Wedge, and Dot Products################## + + @staticmethod + def basic_geometric_product(obj1, obj2): + """ + basic_geometric_product assumes that mv1 and mv2 are both + mulitvectors, not scalars and both are in the base and not the + blade representation. No multivector flags are checked. + This function is used to construct the blades from the bases. + """ + def mul_table(b1, b2): + return MV.base_mul_table[(b1, b2)] + + obj12 = bilinear_product(obj1 * obj2, mul_table) + + return obj12 + + @staticmethod + def geometric_product(b1, b2): + """ + geometric_product(b1, b2) calculates the geometric + product of the multivectors b1 and b2 (b1*b2). + """ + if MV.is_orthogonal: + return MV.product_orthogonal_blades(b1, b2) + else: + result = MV.base_mul_table[(b1, b2)] + return result + + @staticmethod + def basic_add(mv1, mv2): + """ + basic_add assummes that mv1 and mv2 are multivectors both in the + base or blade representation. It sets no flags for the output + and forms mv1.obj+mv2.obj. It is used to form the base expansion + of the blades. + """ + obj = expand(mv1.obj + mv2.obj) + return MV(obj) + + @staticmethod + def basic_sub(mv1, mv2): + """ + basic_sub assummes that mv1 and mv2 are multivectors both in the + base or blade representation. It sets no flags for the output + and forms mv1.obj-mv2.obj. It is used to form the base expansion + of the blades. + """ + obj = expand(mv1.obj - mv2.obj) + return MV(obj) + + @staticmethod + def dot_product(b1, b2): + if MV.is_orthogonal: + return MV.dot_orthogonal_blades(b1, b2) + else: + grade1 = MV.blade_grades[b1] + grade2 = MV.blade_grades[b2] + if MV.dot_mode == 's': # dot product + return MV.blade_dot_table[(b1, b2)] + elif MV.dot_mode == 'l': # left contraction + grade = grade2 - grade1 + elif MV.dot_mode == 'r': # right contraction + grade = grade1 - grade2 + if grade < 0: + return MV() + else: + return MV.blade_dot_table[(b1, b2)] + + @staticmethod + def wedge_product(b1, b2): + i1 = MV.blade_to_index[b1] + i2 = MV.blade_to_index[b2] + i1_plus_i2 = list(i1 + i2) + if len(i1_plus_i2) > MV.dim: + return S.Zero + (sgn, i1_W_i2) = MV.blade_reduce(i1_plus_i2) + if sgn != 0: + return sgn * MV.index_to_blade[tuple(i1_W_i2)] + else: + return S.Zero + + @staticmethod + def blade_reduce(lst): + sgn = 1 + for i in range(1, len(lst)): + save = lst[i] + j = i + while j > 0 and lst[j - 1] > save: + sgn = -sgn + lst[j] = lst[j - 1] + j -= 1 + lst[j] = save + if lst[j] == lst[j - 1]: + return 0, None + return sgn, lst + + @staticmethod + def non_orthogonal_products(mv1, mv2, mode='w'): + if isinstance(mv1, MV) and isinstance(mv2, MV): # both sides are mv + mv1_grades = mv1.get_grades() + mv2_grades = mv2.get_grades() + result = MV() + for grade1 in mv1_grades: + for grade2 in mv2_grades: + if mode == 'w': # wedge product + grade = grade1 + grade2 + elif mode == 's': # dot product + if grade1 == 0: + grade = -1 + elif grade2 == 0: + grade = -1 + else: + grade = abs(grade1 - grade2) + elif mode == 'l': # left contraction + grade = grade2 - grade1 + elif mode == 'r': # right contraction + grade = grade1 - grade2 + if grade >= 0 and grade <= MV.dim: + mv1mv2 = mv1_grades[grade1] * mv2_grades[grade2] + mv1mv2_grades = MV(mv1mv2).get_grades() + if grade in mv1mv2_grades: + result += mv1mv2_grades[grade] + return result + elif isinstance(mv1, MV): # rhs is sympy scalar + if mode == 'w': # wedge product + mv1mv2 = MV(mv1) + mv1mv2.obj = mv2 * mv1.obj + return mv1mv2 + else: # dot product or contractions + return MV() + elif isinstance(mv2, MV): # lhs is sympy scalar + mv1mv2 = MV(mv1) + mv1mv2.obj = mv2 * mv1.obj + return mv1mv2 + else: # both sides are sympy scalars + if mode == 'w': + return MV(mv1 * mv2) + else: + return MV() + + ###################Blade Base conversion functions################## + + def blade_to_base(self): + if MV.is_orthogonal: + return + if self.igrade == 0 or self.igrade == 1: + return self + if self.blade_rep: + self.blade_rep = False + self.obj = expand(self.obj) + self.obj = self.obj.subs({S.One**2: S.One}) + self.obj = simplify(self.obj.subs(MV.blade_expand)) + + return + + def base_to_blade(self): + if MV.is_orthogonal: + return + if self.igrade == 0 or self.igrade == 1: + return + if not self.blade_rep: + self.blade_rep = True + self.obj = expand(self.obj) + self.obj = self.obj.subs(MV.base_expand) + self.obj = expand(self.obj) + self.obj = simplify(self.obj) + return + + @staticmethod + def build_base_blade_arrays(debug): + indexes = tuple(range(MV.dim)) + MV.index = [()] + for i in indexes: + MV.index.append(tuple(combinations(indexes, i + 1))) + MV.index = tuple(MV.index) + + # Set up base and blade and index arrays + + if not MV.is_orthogonal: + MV.bases_flat = [] + MV.bases = [MV.ONE] + MV.base_to_index = {MV.ONE: ()} + MV.index_to_base = {(): MV.ONE} + MV.base_grades = {MV.ONE: 0} + MV.base_grades[S.One] = 0 + + MV.blades = [MV.ONE] + MV.blades_flat = [] + MV.blade_grades = {MV.ONE: 0} + MV.blade_grades[S.One] = 0 + MV.blade_to_index = {MV.ONE: ()} + MV.index_to_blade = {(): MV.ONE} + + ig = 1 # pseudo grade and grade index + for igrade in MV.index[1:]: + if not MV.is_orthogonal: + bases = [] # base symbol array within pseudo grade + blades = [] # blade symbol array within grade + ib = 0 # base index within grade + for ibase in igrade: + # build base name string + (base_sym, base_str, blade_sym, blade_str) = MV.make_base_blade_symbol(ibase) + + if not MV.is_orthogonal: + bases.append(base_sym) + MV.bases_flat.append(base_sym) + + blades.append(blade_sym) + MV.blades_flat.append(blade_sym) + base_index = MV.index[ig][ib] + + # Add to dictionarys relating symbols and indexes + if not MV.is_orthogonal: + MV.base_to_index[base_sym] = base_index + MV.index_to_base[base_index] = base_sym + MV.base_grades[base_sym] = ig + + MV.blade_to_index[blade_sym] = base_index + MV.index_to_blade[base_index] = blade_sym + MV.blade_grades[blade_sym] = ig + + ib += 1 + ig += 1 + + if not MV.is_orthogonal: + MV.bases.append(tuple(bases)) + + MV.blades.append(tuple(blades)) + + if not MV.is_orthogonal: + MV.bases = tuple(MV.bases) + MV.bases_flat = tuple(MV.bases_flat) + MV.bases_flat1 = (MV.ONE, ) + MV.bases_flat + MV.bases_set = set(MV.bases_flat[MV.dim:]) + + MV.bases_flat1_lst = list(MV.bases_flat1) # Python 2.5 + + MV.blades = tuple(MV.blades) + MV.blades_flat = tuple(MV.blades_flat) + MV.blades_flat1 = (MV.ONE, ) + MV.blades_flat + MV.blades_set = set(MV.blades_flat[MV.dim:]) + + MV.blades_flat1_lst = list(MV.blades_flat1) # Python 2.5 + + if debug: + if not MV.is_orthogonal: + oprint('MV Class Global Objects:', None, + 'index(tuple)', MV.index, + 'bases(Symbol)', MV.bases, + 'base_to_index(Symbol->tuple)', MV.base_to_index, + 'index_to_base(tuple->Symbol)', MV.index_to_base, + 'bases flat', MV.bases_flat, + 'bases set', MV.bases_set, + 'blades(Symbol)', MV.blades, + 'blade_grades(int)', MV.blade_grades, + 'blade_to_index(Symbol->tuple)', MV.blade_to_index, + 'index_to_blade(tuple->Symbol)', MV.index_to_blade, + 'blades flat', MV.blades_flat, + 'blades set', MV.blades_set, dict_mode=True) + else: + oprint('MV Class Global Objects:', None, + 'index(tuple)', MV.index, + 'blades(Symbol)', MV.blades, + 'blades flat', MV.blades_flat, + 'blades set', MV.blades_set, + 'blade_grades(int)', MV.blade_grades, + 'blade_to_index(Symbol->tuple)', MV.blade_to_index, + 'index_to_blade(tuple->Symbol)', MV.index_to_blade, dict_mode=True) + return + + @staticmethod + def build_base_mul_table(debug): + + # Calculate geometric product multiplication table for bases + + MV.base_mul_table = {(MV.ONE, MV.ONE): MV.ONE} + + for ig1 in MV.index[1:]: + for ib1 in ig1: + b1 = MV.index_to_base[ib1] + MV.base_mul_table[(MV.ONE, b1)] = b1 + MV.base_mul_table[(b1, MV.ONE)] = b1 + for ig2 in MV.index[1:]: + for ib2 in ig2: + b2 = MV.index_to_base[ib2] + b1b2 = MV.symbol_product_bases(ib1, ib2) + key = (b1, b2) + MV.base_mul_table[key] = simplify(b1b2) + + if debug: + oprint('Geometric Product (*) Table for Bases', MV.base_mul_table, dict_mode=True) + return + + @staticmethod + def build_base_blade_expansion_tables(debug): + + # Expand blades in terms of bases + + MV.blade_expand = {} + for (blade, base) in zip(MV.blades[1], MV.bases[1]): + MV.blade_expand[blade] = base + + sgn = -S.One + igrade = 2 + while igrade <= MV.dim: + for ibase in MV.index[igrade]: + pre_index = (ibase[0], ) + post_index = ibase[1:] + a = MV.index_to_blade[pre_index] + B = MV.index_to_blade[post_index] + B_expand = MV.blade_expand[B] + # a^B = (a*B+sgn*B*a)/2 + if sgn == 1: + result = MV.basic_geometric_product(a, B_expand) + MV.basic_geometric_product(B_expand, a) + else: + result = MV.basic_geometric_product(a, B_expand) - MV.basic_geometric_product(B_expand, a) + MV.blade_expand[MV.index_to_blade[ibase]] = simplify(expand(result / S(2))) + igrade += 1 + sgn = -sgn + + if debug: + oprint('Blade Expansion Table', MV.blade_expand, dict_mode=True) + + # Expand bases in terms of blades + + MV.base_expand = {} + for (blade, base) in zip(MV.blades[1], MV.bases[1]): + MV.base_expand[base] = blade + + ig = 2 + while ig <= MV.dim: + tmp_dict = {} + for ib in MV.index[ig]: + base = MV.index_to_base[ib] + blade = MV.index_to_blade[ib] + tmp = MV.blade_expand[blade] + tmp = tmp.subs({base: -blade}) + tmp = tmp.subs(MV.base_expand) + tmp_dict[base] = simplify(expand(-tmp)) + MV.base_expand.update(tmp_dict) + ig += 1 + + if debug: + oprint('Base Expansion Table', MV.base_expand, dict_mode=True) + + print('Test Blade Expansion:') + for key in MV.blade_expand: + test = MV.blade_expand[key].subs(MV.base_expand) + print(str(key) + ' = ' + str(test)) + print('Test Base Expansion:') + for key in MV.base_expand: + test = MV.base_expand[key].subs(MV.blade_expand) + print(str(key) + ' = ' + str(test)) + + return + + @staticmethod + def build_reciprocal_basis(debug): + MV.I = MV(MV.blades_flat[-1]) + MV.rcpr_norm = get_commutative_coef(simplify((MV.I * MV.I).obj)) + duals = list(MV.blades_flat[-(MV.dim + 1): -1]) + duals.reverse() + sgn = 1 + MV.rcpr_bases_MV = [] + for dual in duals: + recpv = sgn * MV(dual) * MV.I + MV.rcpr_bases_MV.append(recpv) + sgn = -sgn + + if debug: + print('Reciprocal Norm =', MV.rcpr_norm) + oprint('Reciprocal Basis', MV.rcpr_bases_MV) + + if MV.coords is not None: + rcpr_bases_MV = [] + MV.grad = MV() + + result = S.Zero + for (coef, rbase) in zip(MV.coords, MV.rcpr_bases_MV): + nbase_obj = rbase.obj / MV.rcpr_norm + term = coef * rbase + result += term + rcpr_bases_MV.append(MV(nbase_obj)) + + MV.dd_dict = {} + + if MV.is_orthogonal: + bases = MV.blades[1] + else: + bases = MV.bases[1] + + for (coord, base) in zip(MV.coords, bases): + MV.dd_dict[base] = coord + + MV.rcpr_bases_MV = rcpr_bases_MV + + MV.grad.is_grad = True + MV.grad.blade_rep = True + MV.grad.igrade = 1 + MV.grad.rcpr_bases_MV = tuple(rcpr_bases_MV) + MV.grad.coords = MV.coords + MV.grad.norm = MV.rcpr_norm + MV.grad.connection = {} + + if debug: + print('grad =', MV.grad) + oprint('reciprocal bases', MV.rcpr_bases_MV) + + if debug and MV.coords is not None and not MV.is_orthogonal: + print('Reciprocal Vector Test:') + for v1 in MV.blades_MV: + for (v2, rv2) in zip(MV.blades_MV, MV.rcpr_bases_MV): + print(str(v1) + '|Reciprocal(' + str(v2) + ') = ' + str(simplify(expand((v1 | rv2).obj)) / MV.rcpr_norm)) + print('I**2 =', MV.rcpr_norm) + print('Grad Vector:', MV.grad) + + return + + @staticmethod + def build_curvilinear_connection(debug): + """ + Vector.dtau_dict[basis vector symbol,coordinate symbol] = derivative of basis vector as sympy expression + """ + + MV.connection = True + MV.tangent_norm = Vector.norm + MV.tangent_derivatives_MV = {} + + rcpr_bases_MV = [] + + for (rbase, norm) in zip(MV.rcpr_bases_MV, MV.tangent_norm): + rcpr_bases_MV.append(rbase / norm) + + MV.rcpr_bases_MV = rcpr_bases_MV + + for key in Vector.dtau_dict.keys(): + MV.tangent_derivatives_MV[key] = MV(Vector.dtau_dict[key]) + + if debug: + oprint('Tangent Vector Derivatives', MV.tangent_derivatives_MV, dict_mode=True) + + MV.left_connection = {} + MV.right_connection = {} + MV.left_wedge_connection = {} + MV.right_wedge_connection = {} + MV.left_dot_connection = {} + MV.right_dot_connection = {} + + for base in MV.blades[1]: + right_result = MV() + left_result = MV() + for (coord, rblade) in zip(MV.coords, MV.rcpr_bases_MV): + left_result += rblade * MV.tangent_derivatives_MV[(base, coord)] + right_result += MV.tangent_derivatives_MV[(base, coord)] * rblade + left_result.obj = expand(left_result.obj) + right_result.obj = expand(right_result.obj) + left_result.discover_and_set_grade() + right_result.discover_and_set_grade() + MV.left_connection[base] = left_result.obj + MV.right_connection[base] = right_result.obj + MV.left_wedge_connection[base] = left_result.grade(2).obj + MV.right_wedge_connection[base] = right_result.grade(2).obj + MV.left_dot_connection[base] = left_result.grade(0).obj + MV.right_dot_connection[base] = right_result.grade(0).obj + + for grade in MV.blades[2:]: + for blade in grade: + index = MV.blade_to_index[blade] + N = len(index) + left_result = MV() + right_result = MV() + for (coord, rblade) in zip(MV.coords, MV.rcpr_bases_MV): + tmp = MV() + for i in range(N): + i_pre = index[:i] + i_dtan = index[i] + i_post = index[i + 1:] + base = MV.blades[1][i_dtan] + tmp += (MV(MV.index_to_blade[i_pre]) ^ MV.tangent_derivatives_MV[(base, coord)]) ^ MV(MV.index_to_blade[i_post]) + left_result += rblade * tmp + right_result += tmp * rblade + left_result.discover_and_set_grade() + right_result.discover_and_set_grade() + MV.left_connection[blade] = left_result.obj + MV.right_connection[blade] = right_result.obj + MV.left_wedge_connection[blade] = left_result.grade(N + 1).obj + MV.right_wedge_connection[blade] = right_result.grade(N + 1).obj + MV.left_dot_connection[blade] = left_result.grade(abs(N - 1)).obj + MV.right_dot_connection[blade] = right_result.grade(abs(N - 1)).obj + + MV.connection = {'left': MV.left_connection, 'right': MV.right_connection, + 'left_wedge': MV.left_wedge_connection, 'right_wedge': MV.right_wedge_connection, + 'left_dot': MV.left_dot_connection, 'right_dot': MV.right_dot_connection} + MV.grad.connection = MV.connection + + if debug: + oprint('Left Mutlivector Connection', MV.left_connection, + 'Right Mutlivector Connection', MV.right_connection, + 'Left Wedge Mutlivector Connection', MV.left_wedge_connection, + 'Right Wedge Mutlivector Connection', MV.right_wedge_connection, + 'Left Dot Mutlivector Connection', MV.left_dot_connection, + 'Right Dot Mutlivector Connection', MV.right_dot_connection, dict_mode=True) + return + + @staticmethod + def setup(basis, metric=None, coords=None, rframe=False, debug=False, curv=(None, None)): + """ + MV.setup() creates all the arrays and dictionaries required to construct, multiply, add, + and differentiate multivectors in linear and curvilinear coordinate systems. The inputs + to MV.setup() are as follows - + + basis: A string that defines the noncommutative symbols that represent the basis + vectors of the underlying vector space of the multivector space. If the + string consists of substrings separated by spaces or commas each substring + will be the name of the basis vector symbol for example basis='e_x e_y e_z' + or basis='i j k'. Another way to enter the basis symbols is to specify a + base string with a list of subscript strings. This is done with the following + notation so that 'e*x|y|z' is equivalent to 'e_x e_y e_z'. + + metric: + + rframe: + + coords: + + debug: + + curv: + + """ + MV.print_blades = False + MV.connection = False + + MV.ONE = ONE_NC + + MV.basis_vectors = Vector.setup(basis, metric=metric, coords=coords, curv=curv, debug=debug) + MV.curv_norm = curv[1] + MV.metric = Vector.metric + MV.subscripts = Vector.subscripts + MV.coords = Vector.coords + MV.metric2 = 2 * Vector.metric + MV.is_orthogonal = Vector.is_orthogonal + + MV.basis_names = [] + for base in MV.basis_vectors: + MV.basis_names.append(str(base)) + + if debug: + oprint('Basis Names', MV.basis_names) + + MV.dim = len(MV.basis_vectors) + MV.dim1 = MV.dim + 1 + + MV.build_base_blade_arrays(debug) + + if not MV.is_orthogonal: + MV.build_base_mul_table(debug) + MV.build_base_blade_expansion_tables(debug) + + MV.blades_MV = [] + for b in MV.blades[1]: + mv = MV() + mv.obj = b + mv.blade_rep = True + mv.igrade = 1 + MV.blades_MV.append(mv) + MV.build_reciprocal_basis(debug) + + MV.blades_MV = tuple(MV.blades_MV) + + if curv != (None, None): + MV.build_curvilinear_connection(debug) + + MV.print_blades = True + + MV.I = MV(MV.blades_flat[-1]) + MV.Isq = simplify((MV.I * MV.I).scalar()) + MV.Iinv = MV.I / MV.Isq + + if coords is not None: + return MV.blades_MV + (MV.grad, ) + else: + return MV.blades_MV + + +def Format(Fmode=True, Dmode=True, ipy=False): + "Initialize the LaTeX printer with the given mode." + GA_LatexPrinter.Dmode = Dmode + GA_LatexPrinter.Fmode = Fmode + GA_LatexPrinter.ipy = ipy + MV.latex_flg = True + GA_LatexPrinter.redirect(ipy) + return + + +def ga_print_on(): + """ + Turn on the galgebra-aware string printer. + + This function is intended for interactive use only. + Use + + with GA_Printer(): + xxx + + instead of + + ga_print_on() + xxx + ga_print_off() + """ + GA_Printer._on() + return + + +def ga_print_off(): + """ + Turn off the galgebra-aware string printer. + + This function is intended for interactive use only. + See ga_print_on for the noninteractive technique. + """ + GA_Printer._off() + return + + +
    [docs]def DD(v, f): + if isinstance(f, MV): + return f.dd(v) + sf = MV(f, 'scalar') + return sf.dd(v) + +
    +def Nga(x, prec=5): + if isinstance(x, MV): + Px = MV(x) + Px.obj = Nsympy(x.obj, prec) + return Px + else: + return Nsympy(x, prec) + + +def Com(A, B): + "Commutator of A and B divided by 2." + return (A * B - B * A) / S(2) + + +def inv(B): + "Invert B if B*B.rev() is scalar." + Bnorm = B * B.rev() + if Bnorm.is_scalar(): + invB = B.rev() / Bnorm.obj + return invB + else: + raise TypeError('Cannot calculate inverse of ' + str(B) + ' since \n' + + 'B*Brev() = ' + str(Bnorm) + ' is not a scalar.\n') + + +def proj(B, A): + "Project blade A on blade B." + result = (A < B) * inv(B) + result.trigsimp() + return result + + +def rotor(theta, n): + n_sq = (n * n).obj + if n_sq != S.One: + n /= sqrt(n_sq) + N = n.dual() + R = cos(theta) + sin(theta) * N + return R + + +def rot(itheta, A): + "Rotate blade A by angle itheta." + theta = itheta.norm() + i = itheta / theta + result = (cos(theta / 2) - i * sin(theta / 2)) * A * (cos(theta / 2) + i * sin(theta / 2)) + # result.trigsimp(recursive=True) #trigsimp doesn't work for half angle formulas + return result + + +def refl(B, A): + "Reflect blade A in blade B." + j = B.is_blade() + k = A.is_blade() + if j > -1 and k > -1: + result = (-1)**(j * (k + 1)) * B * A * inv(B) + result.trigsimp() + return result + else: + raise ValueError('Can only reflect blades') + + +def dual(M): + return M * MV.Iinv + + +def cross(M1, M2): + return -MV.I * (M1 ^ M2) + + +def ScalarFunction(TheFunction): + return MV() + TheFunction + + +def ReciprocalFrame(basis, mode='norm'): + dim = len(basis) + + indexes = tuple(range(dim)) + index = [()] + + for i in indexes[-2:]: + index.append(tuple(combinations(indexes, i + 1))) + + MFbasis = [] + + for igrade in index[-2:]: + grade = [] + for iblade in igrade: + blade = MV(1, 'scalar') + for ibasis in iblade: + blade ^= basis[ibasis] + blade = blade.trigsimp(deep=True, recursive=True) + grade.append(blade) + MFbasis.append(grade) + E = MFbasis[-1][0] + E_sq = trigsimp((E * E).scalar(), deep=True, recursive=True) + + duals = copy.copy(MFbasis[-2]) + + duals.reverse() + sgn = 1 + rbasis = [] + for dual in duals: + recpv = (sgn * dual * E).trigsimp(deep=True, recursive=True) + rbasis.append(recpv) + sgn = -sgn + + if mode != 'norm': + rbasis.append(E_sq) + else: + for i in range(dim): + rbasis[i] = rbasis[i] / E_sq + + return tuple(rbasis) +
    + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/latest/_modules/sympy/galgebra/debug.html b/latest/_modules/sympy/galgebra/debug.html new file mode 100644 index 000000000000..4b3c7a17278e --- /dev/null +++ b/latest/_modules/sympy/galgebra/debug.html @@ -0,0 +1,249 @@ + + + + + + + + + + sympy.galgebra.debug — SymPy 0.7.6.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    + +

    Source code for sympy.galgebra.debug

    +# sympy/galgebra/debug.py
    +
    +from __future__ import print_function
    +
    +from itertools import chain, islice
    +
    +
    +
    [docs]def ostr(obj, dict_mode=False): + """ + Recursively convert iterated object (list/tuple/dict/set) to string. + """ + def ostr_rec(obj, dict_mode): + global ostr_s + if isinstance(obj, tuple): + if len(obj) == 0: + ostr_s += '(),' + else: + ostr_s += '(' + for obj_i in obj: + ostr_rec(obj_i, dict_mode) + ostr_s = ostr_s[:-1] + '),' + elif isinstance(obj, list): + if len(obj) == 0: + ostr_s += '[],' + else: + ostr_s += '[' + for obj_i in obj: + ostr_rec(obj_i, dict_mode) + ostr_s = ostr_s[:-1] + '],' + elif isinstance(obj, dict): + if dict_mode: + ostr_s += '\n' + for key in obj.keys(): + ostr_rec(key, dict_mode) + if ostr_s[-1] == ',': + ostr_s = ostr_s[:-1] + ostr_s += ' -> ' + ostr_rec(obj[key], dict_mode) + if ostr_s[-1] == ',': + ostr_s = ostr_s[:-1] + ostr_s += '\n' + else: + ostr_s += '{' + for key in obj.keys(): + ostr_rec(key, dict_mode) + if ostr_s[-1] == ',': + ostr_s = ostr_s[:-1] + ostr_s += ':' + ostr_rec(obj[key], dict_mode) + ostr_s = ostr_s[:-1] + '} ' + elif isinstance(obj, set): + tmp_obj = list(obj) + ostr_s += '{' + for obj_i in tmp_obj: + ostr_rec(obj_i, dict_mode) + ostr_s = ostr_s[:-1] + '},' + else: + ostr_s += str(obj) + ',' + return + global ostr_s + ostr_s = '' + if isinstance(obj, (tuple, list, dict, set)): + ostr_rec(obj, dict_mode) + return ostr_s[:-1] + else: + return str(obj) + +
    +
    [docs]def oprint(*args, **kwargs): + """ + Debug printing for iterated (list/tuple/dict/set) objects. args is + of form (title1,object1,title2,object2,...) and prints: + + title1 = object1 + title2 = object2 + ... + + If you only wish to print a title set object = None. + """ + + if 'dict_mode' in kwargs: + dict_mode = kwargs['dict_mode'] + else: + dict_mode = False + + if isinstance(args[0], str) or args[0] is None: + titles = list(islice(args, None, None, 2)) + objs = tuple(islice(args, 1, None, 2)) + if len(args) > 2: + if objs[0] is None: + n = 0 + else: + n = len(titles[0]) + for (title, obj) in zip(titles[1:], objs[1:]): + if obj is not None: + if not (dict_mode and isinstance(obj, dict)): + n = max(n, len(title)) + else: + n = len(titles[0]) + + for (title, obj) in zip(titles, objs): + if obj is None: + print(title) + else: + npad = n - len(title) + if isinstance(obj, dict): + print(title + ':' + ostr(obj, dict_mode)) + else: + print(title + npad * ' ' + ' = ' + ostr(obj, dict_mode)) + else: + for arg in args: + print(ostr(arg, dict_mode)) + return + +
    + + +
    + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/latest/_modules/sympy/galgebra/manifold.html b/latest/_modules/sympy/galgebra/manifold.html new file mode 100644 index 000000000000..6683073d72d0 --- /dev/null +++ b/latest/_modules/sympy/galgebra/manifold.html @@ -0,0 +1,448 @@ + + + + + + + + + + sympy.galgebra.manifold — SymPy 0.7.6.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    + +

    Source code for sympy.galgebra.manifold

    +# sympy/galgebra/manifold.py
    +
    +"""
    +manifold.py defines the Manifold class which allows one to create a
    +vector manifold (manifold defined by vector field of coordinates in
    +embedding vector space) calculate the tangent vectors and derivatives
    +of tangent vectors.
    +
    +Once manifold is created multivector fields can be constructed in the
    +tangent space and all the geometric algebra products and derivatives
    +of the multivector fields calculated.
    +
    +Note that all calculations are done in the embedding space.  Future
    +versions of the code will allow manifolds defined purely in terms of
    +a metric.
    +"""
    +
    +from __future__ import print_function
    +
    +from itertools import combinations
    +from os import system
    +import copy
    +
    +from sympy import trigsimp, simplify
    +
    +from sympy.galgebra.ga import MV
    +from sympy.galgebra.debug import oprint
    +from sympy.galgebra.ncutil import linear_expand
    +from sympy.galgebra.printing import find_executable
    +
    +
    +def fct_to_str(fct_names):
    +    import sys
    +    current_file = open(sys.argv[0], 'r')
    +    file_str = current_file.read()
    +    current_file.close()
    +
    +    if isinstance(fct_names, str):
    +        return fct_names
    +
    +    fcts_str = ''
    +
    +    for fct_name in fct_names:
    +        start_def = file_str.find('\ndef ' + fct_name)
    +        end_def = file_str.find('\ndef ', start_def + 5)
    +        start_class = file_str.find('\nclass ', start_def + 5)
    +        end_def = min(end_def, start_class)
    +        fcts_str += file_str[start_def:end_def]
    +    return fcts_str
    +
    +
    +def VectorComponents(X, basis):
    +    (coefs, bases) = linear_expand(X.obj)
    +    cdict = {}
    +    for (coef, base) in zip(coefs, bases):
    +        cdict[str(base)] = coef
    +    comp = []
    +    for base in basis:
    +        if base in cdict:
    +            comp.append(cdict[base])
    +        else:
    +            comp.append(0)
    +    return comp
    +
    +
    +def FillTemplate(self, template):
    +    Nd = 0
    +    var = []
    +    id_old = 0
    +    while True:
    +        id_new = template.find('$', id_old + 1)
    +        if id_new == -1:
    +            break
    +        Nd += 1
    +        if Nd % 2 == 0:
    +            var.append(template[id_old + 1:id_new])
    +        id_old = id_new
    +
    +    var.sort(reverse=True)
    +
    +    for v in var:
    +        template = template.replace('$' + v + '$', str(eval('self.' + v)))
    +
    +    return template
    +
    +
    +
    [docs]class Manifold: + + def __init__(self, x, coords, debug=False, I=None): + """ + coords: list of coordinate variables + x: vector fuction of coordinate variables (parametric surface) + """ + self.I = I + self.x = x + self.coords = coords + + self.basis = [] + self.basis_str = [] + self.embedded_basis = [] + for u in coords: + tv = x.diff(u) + self.basis.append(tv) + (coefs, bases) = linear_expand(tv.obj) + tc = {} + for (coef, base) in zip(coefs, bases): + str_base = str(base) + tc[str_base] = coef + if str_base not in self.embedded_basis: + self.embedded_basis.append(str_base) + self.basis_str.append(tc) + + self.gij = [] + + for base1 in self.basis: + tmp = [] + for base2 in self.basis: + tmp.append(simplify(trigsimp((base1 | base2).scalar()))) + self.gij.append(tmp) + + for tv in self.basis_str: + for base in self.embedded_basis: + if base not in tv: + tv[base] = 0 + + self.dim = len(self.basis) + + indexes = tuple(range(self.dim)) + self.index = [()] + for i in indexes: + self.index.append(tuple(combinations(indexes, i + 1))) + self.index = tuple(self.index) + + self.MFbasis = [[MV.ONE], self.basis] + + for igrade in self.index[2:]: + grade = [] + for iblade in igrade: + blade = MV(1, 'scalar') + for ibasis in iblade: + blade ^= self.basis[ibasis] + blade = blade.trigsimp(deep=True, recursive=True) + grade.append(blade) + self.MFbasis.append(grade) + self.E = self.MFbasis[-1][0] + self.E_sq = trigsimp((self.E * self.E).scalar(), deep=True, recursive=True) + + duals = copy.copy(self.MFbasis[-2]) + + duals.reverse() + sgn = 1 + self.rbasis = [] + for dual in duals: + recpv = (sgn * dual * self.E).trigsimp(deep=True, recursive=True) + self.rbasis.append(recpv) + sgn = -sgn + + self.dbasis = [] + + for base in self.basis: + dbase = [] + for coord in self.coords: + d = base.diff(coord).trigsimp(deep=True, recursive=True) + dbase.append(d) + self.dbasis.append(dbase) + + self.surface = {} + (coefs, bases) = linear_expand(self.x.obj) + + for (coef, base) in zip(coefs, bases): + self.surface[str(base)] = coef + + self.grad = MV() + self.grad.is_grad = True + self.grad.blade_rep = True + self.grad.igrade = 1 + self.grad.rcpr_bases_MV = [] + for rbase in self.rbasis: + self.grad.rcpr_bases_MV.append(rbase / self.E_sq) + self.grad.rcpr_bases_MV = tuple(self.grad.rcpr_bases_MV) + self.grad.coords = self.coords + self.grad.norm = self.E_sq + self.grad.connection = {} + + if debug: + oprint('x', self.x, + 'coords', self.coords, + 'basis vectors', self.basis, + 'index', self.index, + 'basis blades', self.MFbasis, + 'E', self.E, + 'E**2', self.E_sq, + '*basis', duals, + 'rbasis', self.rbasis, + 'basis derivatives', self.dbasis, + 'surface', self.surface, + 'basis strings', self.basis_str, + 'embedding basis', self.embedded_basis, + 'metric tensor', self.gij) + + def Basis(self): + return tuple(self.basis) + + def Grad(self, F): # Intrisic Derivative + dF = 0 + for (rbase, coord) in zip(self.rbasis, self.coords): + dF += rbase * F.diff(coord) + dF = dF.simplify() + dF = dF / self.E_sq + return dF + + def D(self, F): # Covariant Derivative + dF = self.Grad(F) + return self.Proj(dF) + + def S(self, a): # Shape Tensor + + return + + def Proj(self, F): + PF = (F < self.E) * self.E + PF = PF.simplify() + PF = PF.trigsimp(deep=True, recursive=True) + return (PF / self.E_sq).simplify() + + def Reject(self, F): + return (F - self.Proj(F)).simplify() + + def DD(self, v, f, opstr=False): + mf_comp = [] + for e in self.rbasis: + mf_comp.append((v | e).scalar() / self.E_sq) + result = MV() + op = '' + for (coord, comp) in zip(self.coords, mf_comp): + result += comp * (f.diff(coord)) + if opstr: + op += '(' + str(comp) + ')D{' + str(coord) + '}+' + if opstr: + return str(result), op[:-1] + return result + + def Plot2DSurface(self, u_range, v_range, surf=True, grid=True, tan=1.0, scalar_field=None, skip=[1, 1], fct_def=None): + + plot_template = \ +""" +from numpy import mgrid,shape,swapaxes,zeros,log,exp,sin,cos,tan +$fct_def$ +eps = 1.0e-6 +u_r = $u_range$ +v_r = $v_range$ +$coords$ = mgrid[u_r[0]:u_r[1]+eps:(u_r[1]-u_r[0])/float(u_r[2]-1),\\ + v_r[0]:v_r[1]+eps:(v_r[1]-v_r[0])/float(v_r[2]-1)] +X = $surface$ +scal_tan = $tan$ +x = X['ex'] +y = X['ey'] +z = X['ez'] +du = $basis_str[0]$ +dv = $basis_str[1]$ +Zero = zeros(shape(x)) +if scal_tan > 0.0: + du_x = Zero+du['ex'] + du_y = Zero+du['ey'] + du_z = Zero+du['ez'] + dv_x = Zero+dv['ex'] + dv_y = Zero+dv['ey'] + dv_z = Zero+dv['ez'] + +f = $scalar_field$ +n = $n$ +skip = $skip$ +su = skip[0] +sv = skip[1] +if f[0] != None: + dn_x = f[0]*n[0] + dn_y = f[0]*n[1] + dn_z = f[0]*n[2] + +from mayavi.mlab import plot3d,quiver3d,mesh,figure +figure(bgcolor=(1.0,1.0,1.0)) +if $surf$: + mesh(x,y,z,colormap="gist_earth") +if $grid$: + for i in range(shape(u)[0]): + plot3d(x[i,],y[i,],z[i,],line_width=1.0,color=(0.0,0.0,0.0),tube_radius=None) + + xr = swapaxes(x,0,1) + yr = swapaxes(y,0,1) + zr = swapaxes(z,0,1) + + for i in range(shape(u)[1]): + plot3d(xr[i,],yr[i,],zr[i,],line_width=1.0,color=(0.0,0.0,0.0),tube_radius=None) +if scal_tan > 0.0: + quiver3d(x[::su,::sv],y[::su,::sv],z[::su,::sv],\\ + du_x[::su,::sv],du_y[::su,::sv],du_z[::su,::sv],scale_factor=scal_tan,\\ + line_width=1.0,color=(0.0,0.0,0.0),scale_mode='vector',mode='arrow',resolution=16) + quiver3d(x[::su,::sv],y[::su,::sv],z[::su,::sv],\\ + dv_x[::su,::sv],dv_y[::su,::sv],dv_z[::su,::sv],scale_factor=scal_tan,\\ + line_width=1.0,color=(0.0,0.0,0.0),scale_mode='vector',mode='arrow',resolution=16) +if f[0] != None: + quiver3d(x[::su,::sv],y[::su,::sv],z[::su,::sv],\\ + dn_x[::su,::sv],dn_y[::su,::sv],dn_z[::su,::sv],\\ + line_width=1.0,color=(0.0,0.0,0.0),scale_mode='none',mode='cone',\\ + resolution=16,opacity=0.5) + +""" + if len(self.coords) != 2: + return + + self.skip = skip + self.surf = surf + self.grid = grid + self.tan = tan + if fct_def is None: + self.fct_def = ' ' + else: + self.fct_def = fct_to_str(fct_def) + + self.u_range = u_range + self.v_range = v_range + self.scalar_field = [scalar_field] + + print(self.I, '\n', self.basis[0], '\n', self.basis[1]) + + self.normal = -self.I * (self.basis[0] ^ self.basis[1]) + self.n = VectorComponents(self.normal, ['ex', 'ey', 'ez']) + + msurf = open('manifold_surf.py', 'w') + plot_template = FillTemplate(self, plot_template) + msurf.write(plot_template) + msurf.close() + mayavi2 = find_executable('mayavi2') + if mayavi2 is None: + return + system(mayavi2 + ' manifold_surf.py &') + return
    +
    + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/latest/_modules/sympy/galgebra/ncutil.html b/latest/_modules/sympy/galgebra/ncutil.html new file mode 100644 index 000000000000..dd3140f68fe6 --- /dev/null +++ b/latest/_modules/sympy/galgebra/ncutil.html @@ -0,0 +1,572 @@ + + + + + + + + + + sympy.galgebra.ncutil — SymPy 0.7.6.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    + +

    Source code for sympy.galgebra.ncutil

    +# sympy/galgebra/ncutil.py
    +
    +"""
    +ncutil.py contains all the needed utility functions that only depend on
    +SymPy and that are required for the expansion and manipulation of linear
    +combinations of noncommutative SymPy symbols.
    +
    +also contains "half_angle_reduce" which is probably not needed any more
    +due to the improvements in trigsimp.
    +"""
    +
    +from sympy import expand, Mul, Add, Symbol, S, Pow, diff, trigsimp, \
    +    simplify, sin, cos, symbols
    +
    +try:
    +    from numpy import matrix
    +    numpy_loaded = True
    +except ImportError:
    +    numpy_loaded = False
    +
    +ONE_NC = Symbol('ONE', commutative=False)
    +
    +
    +def get_commutative_coef(expr):
    +    if isinstance(expr, Mul):
    +        (coefs, bases) = expr.args_cnc()
    +        return Mul(*coefs)
    +    return S.One
    +
    +
    +def half_angle_reduce(expr, theta):
    +    s, c = symbols('s c')
    +    sub_dict = {sin(theta / 2): s, cos(theta / 2): c}
    +    new_expr = expr.subs(sub_dict)
    +    sub_dict = {s * c: sin(theta) / 2, s**2: (1 - cos(theta)) / 2, c**2: (1 + cos(theta)) / 2}
    +    # print new_expr
    +    new_expr = trigsimp(simplify(new_expr.subs(sub_dict)), recursive=True)
    +    # print expand(new_expr)
    +    return new_expr
    +
    +
    +
    [docs]def linear_expand(expr): + """ + If a sympy 'Expr' is of the form: + + expr = expr_0 + expr_1*a_1 + ... + expr_n*a_n + + where all the a_j are noncommuting symbols in basis then + + (expr_0, ..., expr_n) and (1, a_1, ..., a_n) are returned. Note that + expr_j*a_j does not have to be of that form, but rather can be any + Mul with a_j as a factor (it doen not have to be a postmultiplier). + expr_0 is the scalar part of the expression. + """ + expr = expand(expr) + if expr.is_commutative: # commutative expr only contains expr_0 + return (expr, ), (S.One, ) + + if isinstance(expr, Mul): # expr only contains one term + (coefs, bases) = expr.args_cnc() + coefs = Mul(*coefs) + bases = bases[0] + elif isinstance(expr, Symbol): # term is Symbol + coefs = S.One + bases = expr + elif isinstance(expr, Add): # expr has multiple terms + coefs = [] + bases = [] + for arg in expr.args: + term = arg.args_cnc() + coef = Mul(*term[0]) + base = term[1][0] + if base in bases: # increment coefficient of base + ibase = list(bases).index(base) # Python 2.5 + coefs[ibase] += coef + else: # add base to list + coefs.append(coef) + bases.append(base) + else: + raise NotImplementedError("linear_expand for type %s" % type(expr)) + + + if not isinstance(coefs, list): # convert single coef to list + coefs = [coefs] + if not isinstance(bases, list): # convert single base to list + bases = [bases] + coefs = tuple(coefs) + bases = tuple(bases) + return coefs, bases + +
    +
    [docs]def linear_projection(expr, plist=None): + """ + If a sympy 'Expr' is of the form: + + expr = expr_0 + expr_1*a_1 + ... + expr_n*a_n + + where all the a_j are noncommuting symbols in basis then + + proj(expr) returns the sum of those terms where a_j is in plist + """ + if expr.is_commutative and plist is None: # return scalar projection + return expr + expr = expand(expr) + if isinstance(expr, Mul): # expr has single term + (coefs, bases) = expr.args_cnc() + if bases[0] in plist: # vector term to be projected + return Mul(*coefs) * bases[0] + else: + return S.Zero + elif isinstance(expr, Symbol): # base vector to be projected + if expr in plist: + return expr + else: + return S.Zero + elif isinstance(expr, Add): # expr has multiple terms + result = S.Zero + for arg in expr.args: + term = arg.args_cnc() + if term[1] == [] and plist is None: # scalar term to be projected + result += Mul(*term[0]) + elif term[1] != [] and plist is not None and term[1][0] in plist: # vector term to be projected + result += Mul(*term[0]) * term[1][0] + return result + +
    +
    [docs]def non_scalar_projection(expr): + """ + If a sympy 'Expr' is of the form: + + expr = expr_0*S.One + expr_1*a_1 + ... + expr_n*a_n + + where all the a_j are noncommuting symbols in basis then + + proj(expr) returns the sum of those terms where a_j is in plist + """ + if expr.is_commutative: # return scalar projection + return S.Zero + expr = expand(expr) + if isinstance(expr, Mul): # expr has single term + (coefs, bases) = expr.args_cnc() + if bases[0] != ONE_NC: # vector term to be projected + return Mul(*coefs) * bases[0] + else: + return S.Zero + elif isinstance(expr, Symbol): # base vector to be projected + if expr != ONE_NC: + return expr + else: + return S.Zero + elif isinstance(expr, Add): # expr has multiple terms + result = S.Zero + for arg in expr.args: + term = arg.args_cnc() + if term[1] != ONE_NC: # vector term to be projected + result += Mul(*term[0]) * term[1][0] + return result + +
    +def nc_substitue(expr, sub_dict): + (coefs, bases) = linear_expand(expr) + result = S.Zero + for (coef, base) in zip(coefs, bases): + if base != 1: + result += coef * sub_dict[base] + return result + + +
    [docs]def linear_function(expr, fct): + """ + If a sympy 'Expr' is of the form: + + expr = expr_0 + expr_1*a_1 + ... + expr_n*a_n + + where all the a_j are noncommuting symbols in basis then + + f(expr) = expr_0 + expr_1*f(a_1) + ... + expr_n*f(a_n) + + is returned + """ + if expr.is_commutative: + return expr + expr = expand(expr) + if isinstance(expr, Mul): + (coefs, bases) = expr.args_cnc() + return Mul(*coefs) * fct(bases[0]) + elif isinstance(expr, Symbol): + return fct(expr) + elif isinstance(expr, Add): + result = S.Zero + for arg in expr.args: + term = arg.args_cnc() + if term[1] == []: + result += Mul(*term[0]) + else: + result += Mul(*term[0]) * fct(term[1][0]) + return result + +
    +
    [docs]def coef_function(expr, fct): + """ + If a sympy 'Expr' is of the form: + + expr = expr_0 + expr_1*a_1 + ... + expr_n*a_n + + where all the a_j are noncommuting symbols in basis then + + f(expr) = fct(expr_0) + fct(expr_1)*a_1 + ... + fct(expr_n)*a_n + + is returned + """ + expr = expand(expr) + if isinstance(expr, Mul): + (coefs, bases) = expr.args_cnc() + return fct(Mul(*coefs)) * bases[0] + elif isinstance(expr, Symbol): + if expr.is_commutative: + return fct(expr) + else: + return expr + elif isinstance(expr, Add): + result = S.Zero + for arg in expr.args: + term = arg.args_cnc() + if term[1] == []: + result += fct(Mul(*term[0])) + else: + result += fct(Mul(*term[0])) * fct(term[1][0]) + return result + +
    +
    [docs]def bilinear_product(expr, fct): + """ + If a sympy 'Expr' is of the form: + + expr = expr_ij*a_i*a_j or expr_0 or expr_i*a_i + + where all the a_i are noncommuting symbols in basis and the expr's + + are commuting expressions then + + bilinear_product(expr) = expr_ij*fct(a_i, a_j) + + bilinear_product(expr_0) = expr_0 + + bilinear_product(expr_i*a_i) = expr_i*a_i + """ + + def bilinear_term(expr, fct): + if expr.is_zero: + return expr + if isinstance(expr, Mul): # bases in expr + (coefs, bases) = expr.args_cnc() + coef = Mul(*tuple(coefs)) + if isinstance(bases[0], Pow): # base is a_i**2 + args = bases[0].args + return coef * fct(args[0], args[0]) + elif len(bases) == 1: # base is a_i + return expr + else: # base is a_i*a_j + return coef * fct(bases[0], bases[1]) + elif isinstance(expr, Pow): # expr is a_i*a_i + args = expr.args + return fct(args[0], args[0]) + elif isinstance(expr, Symbol): + return expr + else: + raise TypeError('!!!!Cannot compute bilinear_product for ' + str(expr) + '!!!!\n') + + expr = expand(expand(expr)) + + if not isinstance(expr, Add): + return bilinear_term(expr, fct) + else: + result = S.Zero + for term in expr.args: + tmp = bilinear_term(term, fct) + result += tmp + return result + +
    +
    [docs]def multilinear_product(expr, fct): + """ + If a sympy 'Expr' is of the form: + + expr = expr_i1i2...irj*a_i1*a_i2*...*a_ir or expr_0 + + where all the a_i are noncommuting symbols in basis and the expr's + + are commuting expressions then + + multilinear_product(expr) = expr_i1i2...ir*fct(a_i1, a_i2, ..., a_ir) + + bilinear_product(expr_0) = expr_0 + + where fct() is defined for r <= n the total number of bases + """ + if expr.is_commutative: # no bases in expr + return expr + if isinstance(expr, Mul): # bases in expr + (coefs, bases) = expr.args_cnc() + if len(coefs) == 0: # expr_ij = 1 + coefs = [S.One] + coef = Mul(*tuple(coefs)) + new_bases = [] + for base in bases: + if isinstance(base, Pow): + args = base.args + new_bases += args[1] * [args[0]] + else: + new_bases.append(base) + return coef * fct(new_bases) + +
    +
    [docs]def bilinear_function(expr, fct): + """ + If a sympy 'Expr' is of the form: + + expr = expr_0 + expr_1*a_1 + ... + expr_n*a_n + expr_11*a_1*a_1 + + ... + expr_ij*a_i*a_j + ... + expr_nn*a_n*a_n + + where all the a_j are noncommuting symbols in basis then + + bilinear_function(expr) = bilinear_product(expr_0) + bilinear_product(expr_1*a_1) + ... + bilinear_product(expr_n*a_n) + + bilinear + product(expr_11*a_1*a_1) + ... + bilinear_product(expr_nn*a_n*a_n) + """ + if expr.is_commutative: + return expr + expr = expand(expr) + if isinstance(expr, (Mul, Pow, Symbol)): # only one additive term + return bilinear_product(expr, fct) + elif isinstance(expr, Add): # multiple additive terms + result = S.Zero + for arg in expr.args: + result += bilinear_product(arg, fct) + return result + +
    +
    [docs]def multilinear_function(expr, fct): + """ + If a sympy 'Expr' is of the form summation convention): + + expr = expr_0 + Sum{0 < r <= n}{expr_i1i2...ir*a_i1*a_i2*...*a_ir} + + where all the a_j are noncommuting symbols in basis then and the + dimension of the basis in n then + + bilinear_function(expr) = multilinear_product(expr_0) + + Sum{0<r<=n}multilinear_product(expr_i1i2...ir*a_i1*a_i2*...*a_ir) + """ + if expr.is_commutative: + return expr + expr = expand(expr) + if isinstance(expr, (Mul, Pow, Symbol)): # only one additive term + return bilinear_product(expr, fct) + elif isinstance(expr, Add): # multiple additive terms + result = S.Zero + for arg in expr.args: + result += bilinear_product(arg, fct) + return result + +
    +
    [docs]def linear_derivation(expr, fct, x): + """ + If a sympy 'Expr' is of the form: + + expr = expr_0 + expr_1*a_1 + ... + expr_n*a_n + + where all the a_j are noncommuting symbols in basis then + + linear_drivation(expr) = diff(expr_0, x) + diff(expr_1, x)*a_1 + ... + + diff(expr_n, x)*a_n + expr_1*fct(a_1, x) + ... + + expr_n*fct(a_n, x) + """ + if expr.is_commutative: + return diff(expr, x) + expr = expand(expr) + if isinstance(expr, Mul): + x = (coefs, bases) = expr.args_cnc() + coef = Mul(*coefs) + return diff(coef, x) * bases[0] + coef * fct(bases[0], x) + elif isinstance(expr, Symbol): + return fct(expr, x) + elif isinstance(expr, Add): + result = S.Zero + for arg in expr.args: + term = arg.args_cnc() + coef = Mul(*term[0]) + if term[1] == []: + result += diff(coef, x) + else: + result += diff(coef, x) * term[1][0] + coef * fct(term[1][0], x) + return result + +
    +
    [docs]def product_derivation(F, fct, x): + """ + If a sympy 'Expr' is of the form: + + expr = expr_0*a_1*...*a_n + + where all the a_j are noncommuting symbols in basis then + + product_derivation(expr) = diff(expr_0, x)*a_1*...*a_n + + expr_0*(fct(a_1, x)*a_2*...*a_n + ... + + a_1*...*a_(i-1)*fct(a_i, x)*a_(i + 1)*...*a_n + ... + + a_1*...*a_(n-1)*fct(a_n, x)) + """ + if F.is_commutative: + return diff(F, x) + elif isinstance(F, Mul): + (coefs, bases) = F.args_cnc() + coef = Mul(*coefs) + dcoef = diff(coef, x) + if len(bases) == 1: + return dcoef * bases[0] + coef * fct(bases[0], x) + else: + result = dcoef * Mul(*bases) + for ib in range(len(bases)): + result += coef * Mul(*bases[:ib]) * fct(bases[ib], x) * Mul(*bases[ib + 1:]) + return result + elif isinstance(F, Symbol): + return fct(F, x) + +
    +
    [docs]def multilinear_derivation(F, fct, x): + """ + If a sympy 'Expr' is of the form (summation convention): + + expr = expr_0 + expr_i1i2...ir*a_i1*...*a_ir + + where all the a_j are noncommuting symbols in basis then + + dexpr = diff(expr_0, x) + d(expr_i1i2...ir*a_i1*...*a_ir) + + is returned where d() is the product derivation + """ + if F.is_commutative: + return diff(F, x) + elif isinstance(F, Mul) or isinstance(F, Symbol): + return product_derivation(F, fct, x) + elif isinstance(F, Add): + result = S.Zero + for term in F.args: + result += product_derivation(term, fct, x) + return result + +
    +def numpy_matrix(M): + if not numpy_loaded: + raise ImportError('Cannot use "numpy_matrix" since "numpy" is not loaded') + Mlst = M.tolist() + nrows = len(Mlst) + ncols = len(Mlst[0]) + for irow in range(nrows): + for icol in range(ncols): + try: + Mlst[irow][icol] = float(Mlst[irow][icol]) + except ValueError: + raise TypeError('In Matrix:\n%s\nCannot convert %s to python float.' % (M, Mlst[irow][icol])) + return matrix(Mlst) +
    + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/latest/_modules/sympy/galgebra/precedence.html b/latest/_modules/sympy/galgebra/precedence.html new file mode 100644 index 000000000000..fdf28ad2729a --- /dev/null +++ b/latest/_modules/sympy/galgebra/precedence.html @@ -0,0 +1,301 @@ + + + + + + + + + + sympy.galgebra.precedence — SymPy 0.7.6.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    + +

    Source code for sympy.galgebra.precedence

    +# sympy/galgebra/precedence.py
    +
    +"""
    +precedence.py converts a string to a multivector expression where the
    +user can control the precedence of the of the multivector operators so
    +that one does not need to put parenthesis around every multivector
    +operation.  The default precedence used (high to low) is <,>, and | have
    +an have the highest precedence, then comes ^, and finally *.
    +"""
    +
    +from __future__ import print_function
    +
    +import re as regrep
    +
    +op_cntrct = regrep.compile(r'(([A-Za-z0-9\_\#]+)(\||<|>)([A-Za-z0-9\_\#]+))')
    +op_wedge = regrep.compile(r'(([A-Za-z0-9\_\#]+)[\^]{1}([A-Za-z0-9\_\#]+)([\^]{1}([A-Za-z0-9\_\#]+))*)')
    +ops = r'[\^\|\<\>]+'
    +ops_search = regrep.compile(r'(\^|\||<|>)+')
    +parse_paren_calls = 0
    +global_dict = {}
    +op_dict = {}
    +op_lst = []
    +
    +OPS = {'<>|': r'(([A-Za-z0-9\_\#]+)(\||<|>)([A-Za-z0-9\_\#]+))',
    +       '^': r'(([A-Za-z0-9\_\#]+)[\^]{1}([A-Za-z0-9\_\#]+)([\^]{1}([A-Za-z0-9\_\#]+))*)',
    +       '*': r'(([A-Za-z0-9\_\#]+)[\*]{1}([A-Za-z0-9\_\#]+)([\*]{1}([A-Za-z0-9\_\#]+))*)'}
    +
    +
    +
    [docs]def define_precedence(gd, op_ord='<>|,^,*'): # Default is Doran and Lasenby convention + global global_dict, op_dict, op_lst + global_dict = gd + op_lst = op_ord.split(',') + op_dict = {} + for op in op_lst: + op_dict[op] = regrep.compile(OPS[op]) + return + +
    +
    [docs]def contains_interval(interval1, interval2): # interval1 inside interval2 + if interval1[0] > interval2[0] and interval1[1] < interval2[1]: + return True + else: + return False + +
    +
    [docs]def parse_paren(line): + global parse_paren_calls + parse_paren_calls += 1 + + if ('(' not in line) or (')' not in line): + return [[[line]]] + level = 0 + max_level = 0 + ich = 0 + paren_lst = [] + for ch in line: + if ch == '(': + level += 1 + paren_lst.append([level, ich]) + if ch == ')': + if level < 1: + raise ValueError('Mismathed Parenthesis in: ' + line + '\n') + paren_lst.reverse() + iparen = 0 + for elem in paren_lst: + if elem[0] == level: + paren_lst[iparen].append(ich) + break + iparen += 1 + paren_lst.reverse() + level -= 1 + max_level = max(max_level, level) + ich += 1 + if level != 0: + raise ValueError('Mismatched Parenthesis in: ' + line + '\n') + if max_level > 0: + level_lst = [] + for x in range(max_level + 1): + level_lst.append([]) + for group in paren_lst: + level_lst[group[0]].append(group[1:]) + ilevel = max_level + while ilevel > 1: + level = level_lst[ilevel] + level_down = level_lst[ilevel - 1] + igroup = 0 + for group in level: + igroup_down = 0 + for group_down in level_down: + if contains_interval(group, group_down): + level_lst[ilevel][igroup].append(igroup_down) + igroup_down += 1 + igroup += 1 + ilevel -= 1 + ilevel = 1 + for level in level_lst[1:]: + igroup = 0 + for group in level: + token = '#' + str(parse_paren_calls) + '_' + str(ilevel) + '_' + str(igroup) + '#' + level_lst[ilevel][igroup].append(line[group[0]:group[1] + 1]) + level_lst[ilevel][igroup].append(token) + igroup += 1 + ilevel += 1 + ilevel = 1 + for level in level_lst[1:]: + igroup = 0 + for group in level: + group.append(group[-2]) + level_lst[ilevel][igroup] = group + igroup += 1 + ilevel += 1 + ilevel = max_level + while ilevel > 1: + igroup = 0 + for group in level_lst[ilevel]: + group_down = level_lst[ilevel - 1][group[2]] + replace_text = group_down[-1].replace(group[-3], group[-2]) + level_lst[ilevel - 1][group[2]][-1] = replace_text + igroup += 1 + ilevel -= 1 + for group in level_lst[1]: + line = line.replace(group[2], group[3]) + ilevel = 1 + level_lst[0] = [[line]] + return level_lst + +
    +
    [docs]def unparse_paren(level_lst): + line = level_lst[0][0][0] + for level in level_lst[1:]: + for group in level: + new_string = group[-1] + if new_string[:2] == '((' and new_string[-2:] == '))': + new_string = new_string[1:-1] + line = line.replace(group[-2], new_string) + return line + +
    +
    [docs]def sub_paren(s): + string = s.group(0) + return '(%s)' % string + +
    +
    [docs]def add_paren(line, re_exprs): + paren_flg = False + if (line[0] == '(') and (line[-1] == ')'): + paren_flg = True + line = line[1:-1] + if ('(' in line) or (')' in line): + line_levels = parse_paren(line) + ilevel = 0 + for level in line_levels: + igroup = 0 + for group in level: + group[-1] = regrep.sub(re_exprs, sub_paren, group[-1]) + line_levels[ilevel][igroup] = group + igroup += 1 + ilevel += 1 + line = unparse_paren(line_levels) + else: + line = regrep.sub(re_exprs, sub_paren, line) + if paren_flg: + line = '(' + line + ')' + return line + +
    +
    [docs]def parse_line(line): + global op_lst, op_dict + line = line.replace(' ', '') + level_lst = parse_paren(line) + ilevel = 0 + for level in level_lst: + igroup = 0 + for group in level: + string = group[-1] + for op in op_lst: + string = add_paren(string, op_dict[op]) + level_lst[ilevel][igroup][-1] = string + igroup += 1 + ilevel += 1 + line = unparse_paren(level_lst) + return line + +
    +
    [docs]def GAeval(s, pstr=False): + seval = parse_line(s) + if pstr: + print(s) + print(seval) + return eval(seval, global_dict)
    +
    + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/latest/_modules/sympy/galgebra/printing.html b/latest/_modules/sympy/galgebra/printing.html new file mode 100644 index 000000000000..bf3beb24a87e --- /dev/null +++ b/latest/_modules/sympy/galgebra/printing.html @@ -0,0 +1,922 @@ + + + + + + + + + + sympy.galgebra.printing — SymPy 0.7.6.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    + +

    Source code for sympy.galgebra.printing

    +# sympy/galgebra/printing.py
    +
    +"""
    +printing.py implements GA_Printer and GA_LatexPrinter classes for
    +multivectors by derivation from the SymPy Printer and LatexPrinter
    +classes.  Where we wish to change the behavior of printing for
    +existing SymPy types the required function have been rewritten
    +and included in GA_Printer and GA_LatexPrinter.  For example
    +we have rewritten:
    +
    +    _print_Derivative
    +    _print_Function
    +    _print_Matrix
    +    _print_Pow
    +    _print_Symbol
    +
    +for GA_LatexPrinter and
    +
    +    _print_Derivative
    +    _print_Function
    +
    +for GA_Printer.
    +
    +There is also and enhanced_print class that allows multivectors,
    +multivector functions, and multivector derivatives to print out
    +in different colors on ansi terminals.
    +"""
    +
    +from __future__ import print_function
    +
    +import os
    +import sys
    +from sympy.core.compatibility import StringIO
    +from sympy.core.decorators import deprecated
    +
    +from sympy import C, S, Basic, Symbol, Matrix
    +from sympy.printing.str import StrPrinter
    +from sympy.printing.latex import LatexPrinter
    +from sympy.printing.conventions import split_super_sub
    +
    +SYS_CMD = {'linux2': {'rm': 'rm', 'evince': 'evince', 'null': ' > /dev/null', '&': '&'},
    +           'win32': {'rm': 'del', 'evince': '', 'null': ' > NUL', '&': ''},
    +           'darwin': {'rm': 'rm', 'evince': 'open', 'null': ' > /dev/null', '&': '&'}}
    +
    +ColorCode = {
    +    'black':        '0;30',         'bright gray':   '0;37',
    +    'blue':         '0;34',         'white':         '1;37',
    +    'green':        '0;32',         'bright blue':   '1;34',
    +    'cyan':         '0;36',         'bright green':  '1;32',
    +    'red':          '0;31',         'bright cyan':   '1;36',
    +    'purple':       '0;35',         'bright red':    '1;31',
    +    'yellow':       '0;33',         'bright purple': '1;35',
    +    'dark gray':    '1;30',         'bright yellow': '1;33',
    +    'normal':       '0'
    +}
    +
    +InvColorCode = dict(zip(ColorCode.values(), ColorCode.keys()))
    +
    +accepted_latex_functions = ['arcsin', 'arccos', 'arctan', 'sin', 'cos', 'tan',
    +                            'theta', 'beta', 'alpha', 'gamma', 'sinh', 'cosh', 'tanh', 'sqrt',
    +                            'ln', 'log', 'sec', 'csc', 'cot', 'coth', 're', 'im', 'frac', 'root',
    +                            'arg', 'zeta']
    +
    +
    +
    [docs]def find_executable(executable, path=None): + """ + Try to find 'executable' in the directories listed in 'path' (a + string listing directories separated by 'os.pathsep'; defaults to + os.environ['PATH']). Returns the complete filename or None if not + found + """ + if path is None: + path = os.environ['PATH'] + paths = path.split(os.pathsep) + extlist = [''] + if os.name == 'os2': + (base, ext) = os.path.splitext(executable) + # executable files on OS/2 can have an arbitrary extension, but + # .exe is automatically appended if no dot is present in the name + if not ext: + executable = executable + ".exe" + elif sys.platform == 'win32': + pathext = os.environ['PATHEXT'].lower().split(os.pathsep) + (base, ext) = os.path.splitext(executable) + if ext.lower() not in pathext: + extlist = pathext + for ext in extlist: + execname = executable + ext + if os.path.isfile(execname): + return execname + else: + for p in paths: + f = os.path.join(p, execname) + if os.path.isfile(f): + return f + else: + return None + +
    +
    [docs]class enhance_print: + """ + A class for color coding the string printing going to a terminal. + """ + + normal = '' + base = '' + fct = '' + deriv = '' + bold = '' + + def __init__(self, base=None, fct=None, deriv=None, on=True, keys=False): + if on: + if 'win' in sys.platform: + + if base is None: + enhance_print.base = ColorCode['blue'] + else: + enhance_print.base = ColorCode[base] + if fct is None: + enhance_print.fct = ColorCode['red'] + else: + enhance_print.fct = ColorCode[fct] + if deriv is None: + enhance_print.deriv = ColorCode['cyan'] + else: + enhance_print.deriv = ColorCode[deriv] + enhance_print.normal = '\033[0m' + + else: + + if base is None: + enhance_print.base = ColorCode['dark gray'] + else: + enhance_print.base = ColorCode[base] + if fct is None: + enhance_print.fct = ColorCode['red'] + else: + enhance_print.fct = ColorCode[fct] + if deriv is None: + enhance_print.deriv = ColorCode['cyan'] + else: + enhance_print.deriv = ColorCode[deriv] + enhance_print.normal = '\033[0m' + + if keys: + print('Enhanced Printing is on:') + print('Base/Blade color is ' + InvColorCode[enhance_print.base]) + print('Function color is ' + InvColorCode[enhance_print.fct]) + print('Derivative color is ' + InvColorCode[enhance_print.deriv] + '\n') + + enhance_print.base = '\033[' + enhance_print.base + 'm' + enhance_print.fct = '\033[' + enhance_print.fct + 'm' + enhance_print.deriv = '\033[' + enhance_print.deriv + 'm' + + @staticmethod + def enhance_base(s): + return enhance_print.base + s + enhance_print.normal + + @staticmethod + def enhance_fct(s): + return enhance_print.fct + s + enhance_print.normal + + @staticmethod + def enhance_deriv(s): + return enhance_print.deriv + s + enhance_print.normal + + @staticmethod + def strip_base(s): + new_s = s.replace(enhance_print.base, '') + new_s = new_s.replace(enhance_print.normal, '') + return new_s + +
    +
    [docs]class GA_Printer(StrPrinter): + """ + An enhanced string printer that is galgebra-aware. + """ + + function_names = ('acos', 'acosh', 'acot', 'acoth', 'arg', 'asin', 'asinh', + 'atan', 'atan2', 'atanh', 'ceiling', 'conjugate', 'cos', + 'cosh', 'cot', 'coth', 'exp', 'floor', 'im', 'log', 're', + 'root', 'sin', 'sinh', 'sqrt', 'sign', 'tan', 'tanh') + + def _print_Function(self, expr): + name = expr.func.__name__ + + if expr.args: + if name in GA_Printer.function_names: + return expr.func.__name__ + "(%s)" % self.stringify(expr.args, ", ") + + return enhance_print.enhance_fct("%s" % (name, )) + + def _print_Derivative(self, expr): + diff_args = list(map(self._print, expr.args)) + return enhance_print.enhance_deriv('D{%s}' % (diff_args[1], )) + '%s' % (diff_args[0], ) + + def _print_MV(self, expr): + if expr.obj.is_zero: + return '0' + else: + if expr.print_blades: + expr.base_to_blade() + ostr = expr.get_normal_order_str() + return ostr + + def _print_Vector(self, expr): + if expr.obj.is_zero: + return '0' + else: + ostr = GA_Printer().doprint(expr.obj) + ostr = ostr.replace(' ', '') + return ostr + + @staticmethod + def _on(): + GA_Printer.Basic__str__ = Basic.__str__ + Basic.__str__ = lambda self: GA_Printer().doprint(self) + return + + @staticmethod + def _off(): + Basic.__str__ = GA_Printer.Basic__str__ + return + + def __enter__(self): + GA_Printer._on() + return self + + def __exit__(self, type, value, traceback): + GA_Printer._off() + + @staticmethod + @deprecated(useinstead="with GA_Printer()", issue=7141, deprecated_since_version="0.7.4") + def on(): + GA_Printer._on() + + @staticmethod + @deprecated(useinstead="with GA_Printer()", issue=7141, deprecated_since_version="0.7.4") + def off(): + GA_Printer._off() + +
    +
    [docs]class GA_LatexPrinter(LatexPrinter): + r""" + An enhanced LaTeX printer that is galgebra-aware. + + The latex printer is turned on with the function (in ga.py) - + + Format(Fmode=True,Dmode=True,ipy=False) + + where Fmode is the function printing mode that surpresses printing arguments, + Dmode is the derivative printing mode that does not use fractions, and + ipy=True is the Ipython notebook mode that does not redirect the print output. + + The latex output is post processed and displayed with the function (in GAPrint.py) - + + xdvi(filename='tmplatex.tex',debug=False) + + where filename is the name of the tex file one would keep for future + inclusion in documents and debug=True would display the tex file + immediately. + + There are three options for printing multivectors in latex. They are + acessed with the multivector member function - + + A.Fmt(self,fmt=1,title=None) + + where fmt=1, 2, or 3 determines whether the entire multivector A is + printed entirely on one line, or one grade is printed per line, or + one base is printed per line. If title is not None then the latex + string generated is of the form - + + title+' = '+str(A) + + where it is assumed that title is a latex math mode string. If title + contains '%' it is treated as a pure latex math mode string. If it + does not contain '%' then the following character mappings are applied - + + - 'grad' replaced by '\\bm{\\nabla} ' + - '*' replaced by '' + - '^' replaced by '\\W ' + - '|' replaced by '\\cdot ' + - '>' replaced by '\\lfloor ' + - '<' replaced by '\\rfloor ' + + In the case of a print statement of the form - + + print(title,A) + + everthing in the title processing still applies except that the multivector + formatting is one multivector per line. + + For print statements of the form - + + print(title) + + where no program variables are printed if title contains '#' then title + is printed as regular latex line. If title does not contain '#' then + title is printed in equation mode. '%' has the same effect in title as + in the Fmt() member function. + """ + + preamble = \ +""" +\\pagestyle{empty} +\\usepackage[latin1]{inputenc} +\\usepackage{amsmath} +\\usepackage{bm} +\\usepackage{amsfonts} +\\usepackage{amssymb} +\\usepackage{amsbsy} +\\usepackage{tensor} +\\usepackage{listings} +\\usepackage{color} +\\definecolor{gray}{rgb}{0.95,0.95,0.95} +\\setlength{\\parindent}{0pt} +\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}} +\\newcommand{\\lp}{\\left (} +\\newcommand{\\rp}{\\right )} +\\newcommand{\\half}{\\frac{1}{2}} +\\newcommand{\\llt}{\\left <} +\\newcommand{\\rgt}{\\right >} +\\newcommand{\\abs}[1]{\\left |{#1}\\right | } +\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}} +\\newcommand{\\lbrc}{\\left \\{} +\\newcommand{\\rbrc}{\\right \\}} +\\newcommand{\\W}{\\wedge} +\\newcommand{\\prm}[1]{{#1}'} +\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}} +\\newcommand{\\R}{\\dagger} +\\newcommand{\\deriv}[3]{\\bfrac{d^{#3}#1}{d{#2}^{#3}}} +\\newcommand{\\grade}[1]{\\left < {#1} \\right >} +\\newcommand{\\f}[2]{{#1}\\lp{#2}\\rp} +\\newcommand{\\eval}[2]{\\left . {#1} \\right |_{#2}} +\\usepackage{float} +\\floatstyle{plain} % optionally change the style of the new float +\\newfloat{Code}{H}{myc} +\\lstloadlanguages{Python} + +\\begin{document} +""" + postscript = '\\end{document}\n' + + Dmode = False # True - Print derivative contracted + Fmode = False # True - Print function contracted + latex_flg = False + + @staticmethod + def redirect(ipy=False): + GA_LatexPrinter.ipy = ipy + GA_LatexPrinter.latex_flg = True + GA_LatexPrinter.Basic__str__ = Basic.__str__ + GA_LatexPrinter.Matrix__str__ = Matrix.__str__ + Basic.__str__ = lambda self: GA_LatexPrinter().doprint(self) + Matrix.__str__ = lambda self: GA_LatexPrinter().doprint(self) + if not ipy: + GA_LatexPrinter.stdout = sys.stdout + sys.stdout = StringIO() + return + + @staticmethod + def restore(): + GA_LatexPrinter.latex_flg = False + if not GA_LatexPrinter.ipy: + sys.stdout = GA_LatexPrinter.stdout + Basic.__str__ = GA_LatexPrinter.Basic__str__ + Matrix.__str__ = GA_LatexPrinter.Matrix__str__ + return + + def _print_Pow(self, expr): + base = self._print(expr.base) + if ('_' in base or '^' in base) and 'cdot' not in base: + mode = True + else: + mode = False + + # Treat x**Rational(1,n) as special case + if expr.exp.is_Rational and abs(expr.exp.p) == 1 and expr.exp.q != 1: + expq = expr.exp.q + + if expq == 2: + tex = r"\sqrt{%s}" % base + elif self._settings['itex']: + tex = r"\root{%d}{%s}" % (expq, base) + else: + tex = r"\sqrt[%d]{%s}" % (expq, base) + + if expr.exp.is_negative: + return r"\frac{1}{%s}" % tex + else: + return tex + elif self._settings['fold_frac_powers'] \ + and expr.exp.is_Rational \ + and expr.exp.q != 1: + base, p, q = self._print(expr.base), expr.exp.p, expr.exp.q + if mode: + return r"{\lp %s \rp}^{%s/%s}" % (base, p, q) + else: + return r"%s^{%s/%s}" % (base, p, q) + + elif expr.exp.is_Rational and expr.exp.is_negative and expr.base.is_Function: + # Things like 1/x + return r"\frac{%s}{%s}" % \ + (1, self._print(C.Pow(expr.base, -expr.exp))) + else: + if expr.base.is_Function: + return self._print(expr.base, self._print(expr.exp)) + else: + if expr.is_commutative and expr.exp == -1: + """ + solves issue 4129 + As Mul always simplify 1/x to x**-1 + The objective is achieved with this hack + first we get the latex for -1 * expr, + which is a Mul expression + """ + tex = self._print(S.NegativeOne * expr).strip() + # the result comes with a minus and a space, so we remove + if tex[:1] == "-": + return tex[1:].strip() + if self._needs_brackets(expr.base): + tex = r"\left(%s\right)^{%s}" + else: + if mode: + tex = r"{\lp %s \rp}^{%s}" + else: + tex = r"%s^{%s}" + + return tex % (self._print(expr.base), + self._print(expr.exp)) + + def _print_Symbol(self, expr): + + def str_symbol(name_str): + (name, supers, subs) = split_super_sub(name_str) + + # translate name, supers and subs to tex keywords + greek = set(['alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', + 'eta', 'theta', 'iota', 'kappa', 'lambda', 'mu', 'nu', + 'xi', 'omicron', 'pi', 'rho', 'sigma', 'tau', 'upsilon', + 'phi', 'chi', 'psi', 'omega']) + + greek_translated = {'lamda': 'lambda', 'Lamda': 'Lambda'} + + other = set(['aleph', 'beth', 'daleth', 'gimel', 'ell', 'eth', + 'hbar', 'hslash', 'mho']) + + def translate(s): + tmp = s.lower() + if tmp in greek or tmp in other: + return "\\" + s + if s in greek_translated: + return "\\" + greek_translated[s] + else: + return s + + name = translate(name) + + if supers != []: + supers = list(map(translate, supers)) + + if subs != []: + subs = list(map(translate, subs)) + + # glue all items together: + if len(supers) > 0: + name += "^{%s}" % " ".join(supers) + if len(subs) > 0: + name += "_{%s}" % " ".join(subs) + + return name + + if expr in self._settings['symbol_names']: + return self._settings['symbol_names'][expr] + + name_str = expr.name + + if '.' in name_str and name_str[0] == '(' and name_str[-1] == ')': + name_str = name_str[1:-1] + name_lst = name_str.split('.') + name_str = r'\lp ' + str_symbol(name_lst[0]) + r'\cdot ' + str_symbol(name_lst[1]) + r'\rp ' + return name_str + + return str_symbol(expr.name) + + def _print_Function(self, expr, exp=None): + func = expr.func.__name__ + + name = func + + if hasattr(self, '_print_' + func): + return getattr(self, '_print_' + func)(expr, exp) + else: + args = [str(self._print(arg)) for arg in expr.args] + # How inverse trig functions should be displayed, formats are: + # abbreviated: asin, full: arcsin, power: sin^-1 + inv_trig_style = self._settings['inv_trig_style'] + # If we are dealing with a power-style inverse trig function + inv_trig_power_case = False + # If it is applicable to fold the argument brackets + can_fold_brackets = self._settings['fold_func_brackets'] and \ + len(args) == 1 and \ + not self._needs_function_brackets(expr.args[0]) + + inv_trig_table = ["asin", "acos", "atan", "acot"] + + # If the function is an inverse trig function, handle the style + if func in inv_trig_table: + if inv_trig_style == "abbreviated": + func = func + elif inv_trig_style == "full": + func = "arc" + func[1:] + elif inv_trig_style == "power": + func = func[1:] + inv_trig_power_case = True + + # Can never fold brackets if we're raised to a power + if exp is not None: + can_fold_brackets = False + + if inv_trig_power_case: + if func in accepted_latex_functions: + name = r"\%s^{-1}" % func + else: + name = r"\operatorname{%s}^{-1}" % func + elif exp is not None: + if func in accepted_latex_functions: + name = r"\%s^{%s}" % (func, exp) + else: + name = latex(Symbol(func)) + if '_' in func or '^' in func: + name = r'{\lp ' + name + r'\rp }^{' + exp + '}' + else: + name += '^{' + exp + '}' + else: + if func in accepted_latex_functions: + name = r"\%s" % func + else: + name = latex(Symbol(func)) + if exp is not None: + if '_' in name or '^' in name: + name = r'\lp ' + name + r'\rp^{' + exp + '}' + else: + name += '^{' + exp + '}' + + if can_fold_brackets: + if func in accepted_latex_functions: + # Wrap argument safely to avoid parse-time conflicts + # with the function name itself + name += r" {%s}" + else: + if not GA_LatexPrinter.Fmode: + name += r"%s" + else: + if func in accepted_latex_functions or not GA_LatexPrinter.Fmode: + name += r"{\left (%s \right )}" + + if inv_trig_power_case and exp is not None: + name += r"^{%s}" % exp + + if func in accepted_latex_functions or not GA_LatexPrinter.Fmode: + if len(args) == 1: + return name % args[0] + else: + return name % ",".join(args) + else: + return name + + def _print_Derivative(self, expr): + dim = len(expr.variables) + + imax = 1 + + if dim == 1: + if GA_LatexPrinter.Dmode: + tex = r"\partial_{%s}" % self._print(expr.variables[0]) + else: + tex = r"\frac{\partial}{\partial %s}" % self._print(expr.variables[0]) + else: + multiplicity, i, tex = [], 1, "" + current = expr.variables[0] + for symbol in expr.variables[1:]: + if symbol == current: + i = i + 1 + else: + multiplicity.append((current, i)) + current, i = symbol, 1 + + else: + imax = max(imax, i) + multiplicity.append((current, i)) + + if GA_LatexPrinter.Dmode and imax == 1: + tmp = '' + dim = 0 + for x, i in multiplicity: + dim += i + tmp += r"%s" % (self._print(x), ) + tex = r"\partial^{%s}_{" % (dim, ) + tmp + '}' + else: + for x, i in multiplicity: + if i == 1: + tex += r"\partial %s" % self._print(x) + else: + tex += r"\partial^{%s} %s" % (i, self._print(x)) + tex = r"\frac{\partial^{%s}}{%s} " % (dim, tex) + + if isinstance(expr.expr, C.AssocOp): + return r"%s\left(%s\right)" % (tex, self._print(expr.expr)) + else: + return r"%s %s" % (tex, self._print(expr.expr)) + + def _print_MV(self, expr): + if expr.obj.is_zero: + return '0 \n' + else: + if expr.print_blades: + expr.base_to_blade() + ostr = expr.get_latex_normal_order_str() + return ostr + + def _print_Matrix(self, expr): + lines = [] + for line in range(expr. rows): # horrible, should be 'rows' + lines.append(" & ".join([self._print(i) for i in expr[line, :]])) + + ncols = 0 + for i in expr[line, :]: + ncols += 1 + + out_str = '\\left [ \\begin{array}{' + ncols * 'c' + '} ' + for line in lines[:-1]: + out_str += line + ' \\\\ ' + out_str += lines[-1] + ' \\end{array}\\right ] ' + return out_str + +
    +
    [docs]def latex(expr, **settings): + "Return the LaTeX representation of the given expression." + return GA_LatexPrinter(settings).doprint(expr) + +
    + +
    [docs]def xdvi(filename=None, debug=False, paper=(14, 11)): + """ + Post processes LaTeX output (see comments below), adds preamble and + postscript, generates tex file, inputs file to latex, displays resulting + pdf file. + """ + if GA_LatexPrinter.ipy: + GA_LatexPrinter.restore(GA_LatexPrinter.ipy) + return + + sys_cmd = SYS_CMD[sys.platform] + + latex_str = sys.stdout.getvalue() + GA_LatexPrinter.restore() + latex_lst = latex_str.split('\n') + latex_str = '' + + lhs = '' + code_flg = False + + for latex_line in latex_lst: + if len(latex_line) > 0 and '##' == latex_line[:2]: + if code_flg: + code_flg = False + latex_line = latex_line[2:] + else: + code_flg = True + latex_line = latex_line[2:] + elif code_flg: + pass + elif len(latex_line) > 0 and '#' in latex_line: # Non equation mode output (comment) + latex_line = latex_line.replace('#', '') + if '%' in latex_line: # Equation mode with no variables to print (comment) + latex_line = latex_line.replace('%', '') + latex_line = '\\begin{equation*} ' + latex_line + ' \\end{equation*}\n' + + else: + latex_line = latex_line.replace('.', r' \cdot ') # For components of metric tensor + if '=' in latex_line: # determing lhs of equation/align + eq_index = latex_line.rindex('=') + 1 + lhs = latex_line[:eq_index] + latex_line = latex_line.replace(lhs, '') + if '%' in lhs: # Do not preprocess lhs of equation/align + lhs = lhs.replace('%', '') + else: # preprocess lhs of equation/align + lhs = lhs.replace('|', r'\cdot ') + lhs = lhs.replace('^', r'\W ') + lhs = lhs.replace('*', ' ') + lhs = lhs.replace('grad', r'\bm{\nabla} ') + lhs = lhs.replace('<<', r'\rfloor ') + lhs = lhs.replace('<', r'\rfloor ') + lhs = lhs.replace('>>', r'\lfloor ') + lhs = lhs.replace('>', r'\lfloor ') + latex_line = lhs + latex_line + + if r'\begin{align*}' in latex_line: # insert lhs of align environment + latex_line = latex_line = latex_line.replace(lhs, '') + latex_line = latex_line.replace(r'\begin{align*}', r'\begin{align*} ' + lhs) + lhs = '' + else: # normal sympy equation + latex_line = latex_line.strip() + if len(latex_line) > 0: + latex_line = '\\begin{equation*} ' + latex_line + ' \\end{equation*}' + + latex_str += latex_line + '\n' + + latex_str = latex_str.replace('\n\n', '\n') + + if debug: + print(latex_str) + + if paper == 'letter': + paper_size = \ +""" +\\documentclass[10pt,fleqn]{report} +""" + else: + paper_size = \ +""" +\\documentclass[10pt,fleqn]{report} +\\usepackage[vcentering]{geometry} +""" + paper_size += '\\geometry{papersize={' + str(paper[0]) + \ + 'in,' + str(paper[1]) + 'in},total={' + str(paper[0] - 1) + \ + 'in,' + str(paper[1] - 1) + 'in}}\n' + + latex_str = paper_size + GA_LatexPrinter.preamble + latex_str + GA_LatexPrinter.postscript + + if filename is None: + pyfilename = sys.argv[0] + rootfilename = pyfilename.replace('.py', '') + filename = rootfilename + '.tex' + + print('latex file =', filename) + + latex_file = open(filename, 'w') + latex_file.write(latex_str) + latex_file.close() + + latex_str = None + + pdflatex = find_executable('pdflatex') + + print('pdflatex path =', pdflatex) + + if pdflatex is not None: + latex_str = 'pdflatex' + else: + return + + if latex_str is not None: + if debug: # Display latex excution output for debugging purposes + os.system(latex_str + ' ' + filename[:-4]) + else: # Works for Linux don't know about Windows + os.system(latex_str + ' ' + filename[:-4] + sys_cmd['null']) + + print_cmd = sys_cmd['evince'] + ' ' + filename[:-4] + '.pdf ' + sys_cmd['&'] + print(print_cmd) + + os.system(print_cmd) + raw_input('!!!!Return to continue!!!!\n') + + if debug: + os.system(sys_cmd['rm'] + ' ' + filename[:-4] + '.aux ' + filename[:-4] + '.log') + else: + os.system(sys_cmd['rm'] + ' ' + filename[:-4] + '.aux ' + filename[:-4] + '.log ' + filename[:-4] + '.tex') + return + +
    +prog_str = '' +off_mode = False + + +
    [docs]def Get_Program(off=False): + global prog_str, off_mode + off_mode = off + if off_mode: + return + prog_file = open(sys.argv[0], 'r') + prog_str = prog_file.read() + prog_file.close() + return + +
    + +
    + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/latest/_modules/sympy/galgebra/stringarrays.html b/latest/_modules/sympy/galgebra/stringarrays.html new file mode 100644 index 000000000000..59c1719947d2 --- /dev/null +++ b/latest/_modules/sympy/galgebra/stringarrays.html @@ -0,0 +1,218 @@ + + + + + + + + + + sympy.galgebra.stringarrays — SymPy 0.7.6.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    + +

    Source code for sympy.galgebra.stringarrays

    +# sympy/galgebra/stringarrays.py
    +
    +"""
    +stringarrays.py are a group of helper functions to convert string
    +input to vector and multivector class function to arrays of SymPy
    +symbols.
    +"""
    +
    +import operator
    +
    +from functools import reduce
    +from itertools import combinations
    +
    +from sympy import S, Symbol, Function
    +
    +
    +
    [docs]def str_array(base, n=None): + """ + Generate one dimensional (list of strings) or two dimensional (list + of list of strings) string array. + + For one dimensional arrays: - + + base is string of variable names separated by blanks such as + base = 'a b c' which produces the string list ['a','b','c'] or + it is a string with no blanks than in conjunction with the + integer n generates - + + str_array('v',n=-3) = ['v_1','v_2','v_3'] + str_array('v',n=3) = ['v__1','v__2','v__3']. + + In the case of LaTeX printing the '_' would give a subscript and + the '__' a super script. + + For two dimensional arrays: - + + base is string where elements are separated by spaces and rows by + commas so that - + + str_array('a b,c d') = [['a','b'],['c','d']] + + """ + if n is None: + if ',' in base: + base_array = [] + base_split = base.split(',') + for base_arg in base_split: + base_array.append(list(filter(lambda x: x != '', base_arg.split(' ')))) + return base_array + else: + return base.split(' ') + result = [] + if isinstance(n, str): + if n[0] == '-': + for index in n[1:].split(' '): + result.append(base + '_' + index) + if n[0] == '+': + for index in n[1:].split(' '): + result.append(base + '__' + index) + if n > 0: + for i in range(1, n + 1): + result.append(base + '__' + str(i)) + if n < 0: + for i in range(1, -n + 1): + result.append(base + '_' + str(i)) + return result + +
    +
    [docs]def symbol_array(base, n=None): + """ + Generates a string arrary with str_array and replaces each string in + array with Symbol of same name. + """ + symbol_str_lst = str_array(base, n) + result = [] + for symbol_str in symbol_str_lst: + result.append(S(symbol_str)) + return tuple(result) + +
    +
    [docs]def fct_sym_array(str_lst, coords=None): + """ + Construct list of symbols or functions with names in 'str_lst'. If + 'coords' are given (tuple of symbols) function list constructed, + otherwise a symbol list is constructed. + """ + if coords is None: + fs_lst = [] + for sym_str in str_lst: + fs_lst.append(Symbol(sym_str)) + else: + fs_lst = [] + for fct_str in str_lst: + fs_lst.append(Function(fct_str)(*coords)) + return fs_lst + +
    +
    [docs]def str_combinations(base, lst, rank=1, mode='_'): + """ + Construct a list of strings of the form 'base+mode+indexes' where the + indexes are formed by converting 'lst' to a list of strings and then + forming the 'indexes' by concatenating combinations of elements from + 'lst' taken 'rank' at a time. + """ + str_lst = list(map(lambda x: base + mode + x, map(lambda x: reduce(operator.add, x), + combinations(map(lambda x: str(x), lst), rank)))) + return str_lst
    +
    + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/latest/_modules/sympy/galgebra/vector.html b/latest/_modules/sympy/galgebra/vector.html new file mode 100644 index 000000000000..9cdf3b1276c2 --- /dev/null +++ b/latest/_modules/sympy/galgebra/vector.html @@ -0,0 +1,424 @@ + + + + + + + + + + sympy.galgebra.vector — SymPy 0.7.6.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    + +

    Source code for sympy.galgebra.vector

    +# sympy/galgebra/vector.py
    +
    +"""
    +vector.py is a helper class for the MV class that defines the basis
    +vectors and metric and calulates derivatives of the basis vectors for
    +the MV class.
    +"""
    +
    +import itertools
    +import copy
    +
    +from sympy import Symbol, S, Matrix, trigsimp, diff, expand
    +
    +from sympy.galgebra.printing import GA_Printer
    +from sympy.galgebra.stringarrays import str_array
    +from sympy.galgebra.ncutil import linear_derivation, bilinear_product
    +from sympy.galgebra.debug import oprint
    +
    +
    +
    [docs]def flatten(lst): + return list(itertools.chain(*lst)) + +
    +
    [docs]def TrigSimp(x): + return trigsimp(x, recursive=True) + +
    +
    [docs]class Vector(object): + """ + Vector class. + + Setup is done by defining a set of basis vectors in static function 'Bases'. + The linear combination of scalar (commutative) sympy quatities and the + basis vectors form the vector space. If the number of basis vectors + is 'n' the metric tensor is formed as an n by n sympy matrix of scalar + symbols and represents the dot products of pairs of basis vectors. + """ + + is_orthogonal = False + + @staticmethod +
    [docs] def setup(base, n=None, metric=None, coords=None, curv=(None, None), debug=False): + """ + Generate basis of vector space as tuple of vectors and + associated metric tensor as Matrix. See str_array(base,n) for + usage of base and n and str_array(metric) for usage of metric. + + To overide elements in the default metric use the character '#' + in the metric string. For example if one wishes the diagonal + elements of the metric tensor to be zero enter metric = '0 #,# 0'. + + If the basis vectors are e1 and e2 then the default metric - + + Vector.metric = ((dot(e1,e1),dot(e1,e2)),dot(e2,e1),dot(e2,e2)) + + becomes - + + Vector.metric = ((0,dot(e1,e2)),(dot(e2,e1),0)). + + The function dot returns a Symbol and is symmetric. + + The functions 'Bases' calculates the global quantities: - + + Vector.basis + tuple of basis vectors + Vector.base_to_index + dictionary to convert base to base inded + Vector.metric + metric tensor represented as a matrix of symbols and numbers + + """ + Vector.is_orthogonal = False + Vector.coords = coords + Vector.subscripts = [] + base_name_lst = base.split(' ') + + # Define basis vectors + + if '*' in base: + base_lst = base.split('*') + base = base_lst[0] + Vector.subscripts = base_lst[1].split('|') + base_name_lst = [] + for subscript in Vector.subscripts: + base_name_lst.append(base + '_' + subscript) + else: + if len(base_name_lst) > 1: + Vector.subscripts = [] + for base_name in base_name_lst: + tmp = base_name.split('_') + Vector.subscripts.append(tmp[-1]) + elif len(base_name_lst) == 1 and Vector.coords is not None: + base_name_lst = [] + for coord in Vector.coords: + Vector.subscripts.append(str(coord)) + base_name_lst.append(base + '_' + str(coord)) + else: + raise TypeError("'%s' does not define basis vectors" % base) + + basis = [] + base_to_index = {} + index = 0 + for base_name in base_name_lst: + basis_vec = Vector(base_name) + basis.append(basis_vec) + base_to_index[basis_vec.obj] = index + index += 1 + + Vector.base_to_index = base_to_index + Vector.basis = tuple(basis) + + # define metric tensor + + default_metric = [] + for bv1 in Vector.basis: + row = [] + for bv2 in Vector.basis: + row.append(Vector.basic_dot(bv1, bv2)) + default_metric.append(row) + Vector.metric = Matrix(default_metric) + if metric is not None: + if metric[0] == '[' and metric[-1] == ']': + Vector.is_orthogonal = True + metric_str_lst = metric[1:-1].split(',') + Vector.metric = [] + for g_ii in metric_str_lst: + Vector.metric.append(S(g_ii)) + Vector.metric = Matrix(Vector.metric) + else: + metric_str_lst = flatten(str_array(metric)) + for index in range(len(metric_str_lst)): + if metric_str_lst[index] != '#': + Vector.metric[index] = S(metric_str_lst[index]) + + Vector.metric_dict = {} # Used to calculate dot product + N = range(len(Vector.basis)) + if Vector.is_orthogonal: + for ii in N: + Vector.metric_dict[Vector.basis[ii].obj] = Vector.metric[ii] + else: + for irow in N: + for icol in N: + Vector.metric_dict[(Vector.basis[irow].obj, Vector.basis[icol].obj)] = Vector.metric[irow, icol] + + # calculate tangent vectors and metric for curvilinear basis + + if curv != (None, None): + X = S.Zero + for (coef, base) in zip(curv[0], Vector.basis): + X += coef * base.obj + Vector.tangents = [] + for (coord, norm) in zip(Vector.coords, curv[1]): + tau = diff(X, coord) + tau = trigsimp(tau) + tau /= norm + tau = expand(tau) + Vtau = Vector() + Vtau.obj = tau + Vector.tangents.append(Vtau) + metric = [] + for tv1 in Vector.tangents: + row = [] + for tv2 in Vector.tangents: + row.append(tv1 * tv2) + metric.append(row) + metric = Matrix(metric) + metric = metric.applyfunc(TrigSimp) + Vector.metric_dict = {} + if metric.is_diagonal: + Vector.is_orthogonal = True + tmp_metric = [] + for ii in N: + tmp_metric.append(metric[ii, ii]) + Vector.metric_dict[Vector.basis[ii].obj] = metric[ii, ii] + Vector.metric = Matrix(tmp_metric) + else: + Vector.is_orthogonal = False + Vector.metric = metric + for irow in N: + for icol in N: + Vector.metric_dict[(Vector.basis[irow].obj, Vector.basis[icol].obj)] = Vector.metric[irow, icol] + Vector.norm = curv[1] + + if debug: + oprint('Tangent Vectors', Vector.tangents, + 'Metric', Vector.metric, + 'Metric Dictionary', Vector.metric_dict, + 'Normalization', Vector.norm, dict_mode=True) + + # calculate derivatives of tangent vectors + + Vector.dtau_dict = None + dtau_dict = {} + + for x in Vector.coords: + for (tau, base) in zip(Vector.tangents, Vector.basis): + dtau = tau.diff(x).applyfunc(TrigSimp) + result = S.Zero + for (t, b) in zip(Vector.tangents, Vector.basis): + t_dtau = TrigSimp(t * dtau) + result += t_dtau * b.obj + dtau_dict[(base.obj, x)] = result + + Vector.dtau_dict = dtau_dict + + if debug: + oprint('Basis Derivatives', Vector.dtau_dict, dict_mode=True) + + return tuple(Vector.basis) +
    + def __init__(self, basis_str=None): + if isinstance(basis_str, Vector): + self.obj = basis_str + else: + if basis_str is None or basis_str == '0': + self.obj = S(0) + else: + self.obj = Symbol(basis_str, commutative=False) + + """ + def diff(self, x): + (coefs, bases) = linear_expand(self.obj) + result = S.Zero + for (coef, base) in zip(coefs, bases): + result += diff(coef, x) * base + return result + """ + + def diff(self, x): + Dself = Vector() + if isinstance(Vector.dtau_dict, dict): + Dself.obj = linear_derivation(self.obj, Vector.Diff, x) + else: + Dself.obj = diff(self.obj, x) + return Dself + + @staticmethod +
    [docs] def basic_dot(v1, v2): + """ + Dot product of two basis vectors returns a Symbol + """ + i1 = list(Vector.basis).index(v1) # Python 2.5 + i2 = list(Vector.basis).index(v2) # Python 2.5 + if i1 < i2: + dot_str = '(' + str(Vector.basis[i1]) + '.' + str(Vector.basis[i2]) + ')' + else: + dot_str = '(' + str(Vector.basis[i2]) + '.' + str(Vector.basis[i1]) + ')' + return Symbol(dot_str) +
    + @staticmethod + def dot(b1, b2): + if Vector.is_orthogonal: + if b1 != b2: + return S.Zero + else: + return Vector.metric_dict[b1] + else: + return Vector.metric_dict[(b1, b2)] + + @staticmethod + def Diff(b, x): + return Vector.dtau_dict[(b, x)] + + ######################## Operator Definitions####################### + + def __str__(self): + return GA_Printer().doprint(self) + + def __mul__(self, v): + if not isinstance(v, Vector): + self_x_v = Vector() + self_x_v.obj = self.obj * v + return self_x_v + else: + result = expand(self.obj * v.obj) + result = bilinear_product(result, Vector.dot) + return result + + def __rmul__(self, s): + s_x_self = Vector() + s_x_self.obj = s * self.obj + return s_x_self + + def __add__(self, v): + self_p_v = Vector() + self_p_v.obj = self.obj + v.obj + return self_p_v + + def __add_ab__(self, v): + self.obj += v.obj + return + + def __sub__(self, v): + self_m_v = Vector() + self_m_v.obj = self.obj - v.obj + return self_m_v + + def __sub_ab__(self, v): + self.obj -= v.obj + return + + def __pos__(self): + return self + + def __neg__(self): + n_self = copy.deepcopy(self) + n_self.obj = -self.obj + return n_self + + def applyfunc(self, fct): + fct_self = Vector() + fct_self.obj = fct(self.obj) + return fct_self
    +
    + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/latest/_modules/sympy/geometry/point3d.html b/latest/_modules/sympy/geometry/point3d.html new file mode 100644 index 000000000000..45a49965996c --- /dev/null +++ b/latest/_modules/sympy/geometry/point3d.html @@ -0,0 +1,700 @@ + + + + + + + + + + sympy.geometry.point3d — SymPy 0.7.6.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    + +

    Source code for sympy.geometry.point3d

    +"""Geometrical Points.
    +
    +Contains
    +========
    +Point3D
    +
    +"""
    +
    +from __future__ import print_function, division
    +
    +from sympy.core import S, sympify
    +from sympy.core.compatibility import iterable
    +from sympy.core.containers import Tuple
    +from sympy.simplify import simplify, nsimplify
    +from sympy.geometry.exceptions import GeometryError
    +from sympy.geometry.point import Point
    +from sympy.functions.elementary.miscellaneous import sqrt
    +from sympy.functions.elementary.complexes import im
    +from .entity import GeometryEntity
    +from sympy.matrices import Matrix
    +from sympy.core.numbers import Float
    +from sympy.core.evaluate import global_evaluate
    +
    +
    +
    [docs]class Point3D(GeometryEntity): + """A point in a 3-dimensional Euclidean space. + + Parameters + ========== + + coords : sequence of 3 coordinate values. + + Attributes + ========== + + x + y + z + length + + Raises + ====== + + NotImplementedError + When trying to create a point other than 2 or 3 dimensions. + When `intersection` is called with object other than a Point. + TypeError + When trying to add or subtract points with different dimensions. + + Notes + ===== + + Currently only 2-dimensional and 3-dimensional points are supported. + + Examples + ======== + + >>> from sympy import Point3D + >>> from sympy.abc import x + >>> Point3D(1, 2, 3) + Point3D(1, 2, 3) + >>> Point3D([1, 2, 3]) + Point3D(1, 2, 3) + >>> Point3D(0, x, 3) + Point3D(0, x, 3) + + Floats are automatically converted to Rational unless the + evaluate flag is False: + + >>> Point3D(0.5, 0.25, 2) + Point3D(1/2, 1/4, 2) + >>> Point3D(0.5, 0.25, 3, evaluate=False) + Point3D(0.5, 0.25, 3) + + """ + def __new__(cls, *args, **kwargs): + eval = kwargs.get('evaluate', global_evaluate[0]) + if isinstance(args[0], (Point, Point3D)): + if not eval: + return args[0] + args = args[0].args + elif isinstance(args[0], Point): + args = args[0].args + else: + if iterable(args[0]): + args = args[0] + if len(args) not in (2, 3): + raise TypeError( + "Enter a 2 or 3 dimensional point") + coords = Tuple(*args) + if len(coords) == 2: + coords += (S.Zero,) + if eval: + coords = coords.xreplace(dict( + [(f, simplify(nsimplify(f, rational=True))) + for f in coords.atoms(Float)])) + return GeometryEntity.__new__(cls, *coords) + + def __contains__(self, item): + return item == self + + @property +
    [docs] def x(self): + """ + Returns the X coordinate of the Point. + + Examples + ======== + + >>> from sympy import Point3D + >>> p = Point3D(0, 1, 3) + >>> p.x + 0 + """ + return self.args[0] +
    + @property +
    [docs] def y(self): + """ + Returns the Y coordinate of the Point. + + Examples + ======== + + >>> from sympy import Point3D + >>> p = Point3D(0, 1, 2) + >>> p.y + 1 + """ + return self.args[1] +
    + @property +
    [docs] def z(self): + """ + Returns the Z coordinate of the Point. + + Examples + ======== + + >>> from sympy import Point3D + >>> p = Point3D(0, 1, 1) + >>> p.z + 1 + """ + return self.args[2] +
    + @property +
    [docs] def length(self): + """ + Treating a Point as a Line, this returns 0 for the length of a Point. + + Examples + ======== + + >>> from sympy import Point3D + >>> p = Point3D(0, 1, 1) + >>> p.length + 0 + """ + return S.Zero +
    +
    [docs] def direction_ratio(self, point): + """ + Gives the direction ratio between 2 points + + Parameters + ========== + + p : Point3D + + Returns + ======= + + list + + Examples + ======== + + >>> from sympy import Point3D + >>> p1 = Point3D(1, 2, 3) + >>> p1.direction_ratio(Point3D(2, 3, 5)) + [1, 1, 2] + """ + return [(point.x - self.x),(point.y - self.y),(point.z - self.z)] +
    +
    [docs] def direction_cosine(self, point): + """ + Gives the direction cosine between 2 points + + Parameters + ========== + + p : Point3D + + Returns + ======= + + list + + Examples + ======== + + >>> from sympy import Point3D + >>> p1 = Point3D(1, 2, 3) + >>> p1.direction_cosine(Point3D(2, 3, 5)) + [sqrt(6)/6, sqrt(6)/6, sqrt(6)/3] + """ + a = self.direction_ratio(point) + b = sqrt(sum(i**2 for i in a)) + return [(point.x - self.x) / b,(point.y - self.y) / b, + (point.z - self.z) / b] +
    + @staticmethod +
    [docs] def are_collinear(*points): + """Is a sequence of points collinear? + + Test whether or not a set of points are collinear. Returns True if + the set of points are collinear, or False otherwise. + + Parameters + ========== + + points : sequence of Point + + Returns + ======= + + are_collinear : boolean + + See Also + ======== + + sympy.geometry.line3d.Line3D + + Examples + ======== + + >>> from sympy import Point3D, Matrix + >>> from sympy.abc import x + >>> p1, p2 = Point3D(0, 0, 0), Point3D(1, 1, 1) + >>> p3, p4, p5 = Point3D(2, 2, 2), Point3D(x, x, x), Point3D(1, 2, 6) + >>> Point3D.are_collinear(p1, p2, p3, p4) + True + >>> Point3D.are_collinear(p1, p2, p3, p5) + False + """ + # Coincident points are irrelevant and can confuse this algorithm. + # Use only unique points. + points = list(set(points)) + if not all(isinstance(p, Point3D) for p in points): + raise TypeError('Must pass only 3D Point objects') + + if len(points) < 2: + return False + if len(points) == 2: + return True # two points always form a line + if len(points) == 3: + a = (points[0].direction_cosine(points[1])) + b = (points[0].direction_cosine(points[2])) + a = [abs(i) for i in a] + b = [abs(i) for i in b] + if a == b: + return True + else: + return False + # XXX Cross product is used now, + # If the concept needs to extend to more than three + # dimensions then another method would have to be used + for i in range(len(points) - 2): + pv1 = [j - k for j, k in zip(points[i].args, \ + points[i + 1].args)] + pv2 = [j - k for j, k in zip(points[i + 1].args, + points[i + 2].args)] + rank = Matrix([pv1, pv2]).rank() + if (rank != 1): + return False + return True +
    + @staticmethod +
    [docs] def are_coplanar(*points): + """ + + This function tests whether passed points are coplanar or not. + It uses the fact that the triple scalar product of three vectors + vanishes if the vectors are coplanar. Which means that the volume + of the solid described by them will have to be zero for coplanarity. + + Parameters + ========== + + A set of points 3D points + + Returns + ======= + + boolean + + Examples + ======== + + >>> from sympy import Point3D + >>> p1 = Point3D(1, 2, 2) + >>> p2 = Point3D(2, 7, 2) + >>> p3 = Point3D(0, 0, 2) + >>> p4 = Point3D(1, 1, 2) + >>> Point3D.are_coplanar(p1, p2, p3, p4) + True + >>> p5 = Point3D(0, 1, 3) + >>> Point3D.are_coplanar(p1, p2, p3, p5) + False + + """ + from sympy.geometry.plane import Plane + points = list(set(points)) + if len(points) < 3: + raise ValueError('At least 3 points are needed to define a plane.') + a, b = points[:2] + for i, c in enumerate(points[2:]): + try: + p = Plane(a, b, c) + for j in (0, 1, i): + points.pop(j) + return all(p.is_coplanar(i) for i in points) + except NotImplementedError: # XXX should be ValueError + pass + raise ValueError('At least 3 non-collinear points needed to define plane.') +
    +
    [docs] def distance(self, p): + """The Euclidean distance from self to point p. + + Parameters + ========== + + p : Point + + Returns + ======= + + distance : number or symbolic expression. + + See Also + ======== + + sympy.geometry.line.Segment.length + + Examples + ======== + + >>> from sympy import Point3D + >>> p1, p2 = Point3D(1, 1, 1), Point3D(4, 5, 0) + >>> p1.distance(p2) + sqrt(26) + + >>> from sympy.abc import x, y, z + >>> p3 = Point3D(x, y, z) + >>> p3.distance(Point3D(0, 0, 0)) + sqrt(x**2 + y**2 + z**2) + + """ + p = Point3D(p) + return sqrt(sum([(a - b)**2 for a, b in zip(self.args, p.args)])) +
    +
    [docs] def midpoint(self, p): + """The midpoint between self and point p. + + Parameters + ========== + + p : Point + + Returns + ======= + + midpoint : Point + + See Also + ======== + + sympy.geometry.line.Segment.midpoint + + Examples + ======== + + >>> from sympy import Point3D + >>> p1, p2 = Point3D(1, 1, 1), Point3D(13, 5, 1) + >>> p1.midpoint(p2) + Point3D(7, 3, 1) + + """ + p = Point3D(p) + return Point3D([simplify((a + b)*S.Half) for a, b in + zip(self.args, p.args)]) +
    +
    [docs] def evalf(self, prec=None, **options): + """Evaluate the coordinates of the point. + + This method will, where possible, create and return a new Point + where the coordinates are evaluated as floating point numbers to + the precision indicated (default=15). + + Returns + ======= + + point : Point + + Examples + ======== + + >>> from sympy import Point3D, Rational + >>> p1 = Point3D(Rational(1, 2), Rational(3, 2), Rational(5, 2)) + >>> p1 + Point3D(1/2, 3/2, 5/2) + >>> p1.evalf() + Point3D(0.5, 1.5, 2.5) + + """ + coords = [x.evalf(prec, **options) for x in self.args] + return Point3D(*coords, evaluate=False) +
    + n = evalf + +
    [docs] def intersection(self, o): + """The intersection between this point and another point. + + Parameters + ========== + + other : Point + + Returns + ======= + + intersection : list of Points + + Notes + ===== + + The return value will either be an empty list if there is no + intersection, otherwise it will contain this point. + + Examples + ======== + + >>> from sympy import Point3D + >>> p1, p2, p3 = Point3D(0, 0, 0), Point3D(1, 1, 1), Point3D(0, 0, 0) + >>> p1.intersection(p2) + [] + >>> p1.intersection(p3) + [Point3D(0, 0, 0)] + + """ + if isinstance(o, Point3D): + if self == o: + return [self] + return [] + + return o.intersection(self) +
    +
    [docs] def scale(self, x=1, y=1, z=1, pt=None): + """Scale the coordinates of the Point by multiplying by + ``x`` and ``y`` after subtracting ``pt`` -- default is (0, 0) -- + and then adding ``pt`` back again (i.e. ``pt`` is the point of + reference for the scaling). + + See Also + ======== + + translate + + Examples + ======== + + >>> from sympy import Point3D + >>> t = Point3D(1, 1, 1) + >>> t.scale(2) + Point3D(2, 1, 1) + >>> t.scale(2, 2) + Point3D(2, 2, 1) + + """ + if pt: + pt = Point3D(pt) + return + self.translate(*(-pt).args).scale(x, y, z).translate(*pt.args) + return Point3D(self.x*x, self.y*y, self.z*z) +
    +
    [docs] def translate(self, x=0, y=0, z=0): + """Shift the Point by adding x and y to the coordinates of the Point. + + See Also + ======== + + rotate, scale + + Examples + ======== + + >>> from sympy import Point3D + >>> t = Point3D(0, 1, 1) + >>> t.translate(2) + Point3D(2, 1, 1) + >>> t.translate(2, 2) + Point3D(2, 3, 1) + >>> t + Point3D(2, 2, 2) + Point3D(2, 3, 3) + + """ + return Point3D(self.x + x, self.y + y, self.z + z) +
    +
    [docs] def transform(self, matrix): + """Return the point after applying the transformation described + by the 3x3 Matrix, ``matrix``. + + See Also + ======== + geometry.entity.rotate + geometry.entity.scale + geometry.entity.translate + """ + x, y, z = self.args + return Point3D(*(Matrix(1, 3, [x, y, z])*matrix).tolist()[0][:2]) +
    +
    [docs] def dot(self, p2): + """Return dot product of self with another Point.""" + p2 = Point3D(p2) + x1, y1, z1 = self.args + x2, y2, z2 = p2.args + return x1*x2 + y1*y2 + z1*z2 +
    + def equals(self, other): + if not isinstance(other, Point3D): + return False + return all(a.equals(b) for a, b in zip(self.args, other.args)) + + def __add__(self, other): + """Add other to self by incrementing self's coordinates by those of + other. + + See Also + ======== + + sympy.geometry.entity.translate + + """ + + if isinstance(other, Point3D): + if len(other.args) == len(self.args): + return Point3D(*[simplify(a + b) for a, b in + zip(self.args, other.args)]) + else: + raise TypeError( + "Points must have the same number of dimensions") + else: + raise ValueError('Cannot add non-Point, %s, to a Point' % other) + + def __sub__(self, other): + """Subtract two points, or subtract a factor from this point's + coordinates.""" + if isinstance(other, Point3D): + if len(other.args) == len(self.args): + return Point3D(*[simplify(a - b) for a, b in + zip(self.args, other.args)]) + else: + raise TypeError( + "Points must have the same number of dimensions") + else: + raise ValueError('Cannot subtract non-Point, %s, to a Point' + % other) + + def __mul__(self, factor): + """Multiply point's coordinates by a factor.""" + factor = sympify(factor) + return Point3D([x*factor for x in self.args]) + + def __div__(self, divisor): + """Divide point's coordinates by a factor.""" + divisor = sympify(divisor) + return Point3D([x/divisor for x in self.args]) + + __truediv__ = __div__ + + def __neg__(self): + """Negate the point.""" + return Point3D([-x for x in self.args]) + + def __abs__(self): + """Returns the distance between this point and the origin.""" + origin = Point3D([0]*len(self.args)) + return Point3D.distance(origin, self)
    +
    + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/latest/_sources/modules/galgebra/GA.txt b/latest/_sources/modules/galgebra/GA.txt new file mode 100644 index 000000000000..4c40080cc38a --- /dev/null +++ b/latest/_sources/modules/galgebra/GA.txt @@ -0,0 +1,2715 @@ +.. raw:: html + + + +.. role:: red + :class: color:red + + +***************** +Geometric Algebra +***************** + +:Author: Alan Bromborsky + +.. |release| replace:: 0.10 + +.. % Complete documentation on the extended LaTeX markup used for Python +.. % documentation is available in ``Documenting Python'', which is part +.. % of the standard documentation for Python. It may be found online +.. % at: +.. % +.. % http://www.python.org/doc/current/doc/doc.html +.. % \lstset{language=Python} +.. % \input{macros} +.. % This is a template for short or medium-size Python-related documents, +.. % mostly notably the series of HOWTOs, but it can be used for any +.. % document you like. +.. % The title should be descriptive enough for people to be able to find +.. % the relevant document. + +.. % Increment the release number whenever significant changes are made. +.. % The author and/or editor can define 'significant' however they like. + +.. % At minimum, give your name and an email address. You can include a +.. % snail-mail address if you like. + +.. % This makes the Abstract go on a separate page in the HTML version; +.. % if a copyright notice is used, it should go immediately after this. +.. % +.. % \ifhtml +.. % \chapter*{Front Matter\label{front}} +.. % \fi +.. % Copyright statement should go here, if needed. +.. % ... +.. % The abstract should be a paragraph or two long, and describe the + +.. % scope of the document. + +.. topic:: Abstract + + This document describes the implementation, installation and use of a + geometric algebra module written in + python that utilizes the :mod:`sympy` symbolic algebra library. The python + module *galgebra* has been developed for coordinate free calculations using + the operations (geometric, outer, and inner products etc.) of geometric algebra. + The operations can be defined using a completely arbitrary metric defined + by the inner products of a set of arbitrary vectors or the metric can be + restricted to enforce orthogonality and signature constraints on the set of + vectors. In addition the module includes the geometric, outer (curl) and inner + (div) derivatives and the ability to define a curvilinear coordinate system. + The module requires the sympy module and the numpy module for numerical linear + algebra calculations. For latex output a latex distribution must be installed. + + +.. module:: sympy.galgebra.ga + + +What is Geometric Algebra? +========================== + +Geometric algebra is the Clifford algebra of a real finite dimensional vector +space or the algebra that results when a real finite dimensional vector space +is extended with a product of vectors (geometric product) that is associative, +left and right distributive, and yields a real number for the square (geometric +product) of any vector [Hestenes]_, [Doran]_. The elements of the geometric +algebra are called multivectors and consist of the linear combination of +scalars, vectors, and the geometric product of two or more vectors. The +additional axioms for the geometric algebra are that for any vectors :math:`a`, +:math:`b`, and :math:`c` in the base vector space ([Doran]_,p85): + +.. math:: + :nowrap: + + \begin{equation*} + \begin{array}{c} + a\left ( bc \right ) = \left ( ab \right ) c \\ + a\left ( b+c \right ) = ab+ac \\ + \left ( a + b \right ) c = ac+bc \\ + aa = a^{2} \in \Re + \end{array} + \end{equation*} + + +By induction the first three axioms also apply to any multivectors. The dot product of +two vectors is defined by ([Doran]_,p86) + +.. math:: + :nowrap: + + \begin{equation*} + a\cdot b \equiv (ab+ba)/2 + \end{equation*} + + +Then consider + +.. math:: + :nowrap: + + \begin{align*} + c &= a+b \\ + c^{2} &= (a+b)^{2} \\ + c^{2} &= a^{2}+ab+ba+b^{2} \\ + a\cdot b &= (c^{2}-a^{2}-b^{2})/2 \in \Re + \end{align*} + + +Thus :math:`a\cdot b` is real. The objects generated from linear combinations +of the geometric products of vectors are called multivectors. If a basis for +the underlying vector space is the set of vectors formed from :math:`e_{1},\dots,e_{n}` +a complete basis for the geometric algebra is given by the scalar :math:`1`, the vectors :math:`e_{1},\dots,e_{n}` +and all geometric products of vectors + +.. math:: + :nowrap: + + \begin{equation*} + e_{i_{1}} e_{i_{2}} \dots e_{i_{r}} \mbox{ where } 0 \le r \le n \mbox{, } 0 \le i_{j} \le n \mbox{ and } 0 < i_{1} < i_{2} < \dots < i_{r} \le n + \end{equation*} + + +Each base of the complete basis is represented by a noncommutative symbol (except for the scalar 1) +with name :math:`e_{i_{1}}\dots e_{i_{r}}` so that the general multivector :math:`\boldsymbol{A}` is represented by +(:math:`A` is the scalar part of the multivector and the :math:`A^{i_{1},\dots,i_{r}}` are scalars) + +.. math:: + :nowrap: + + \begin{equation*} + \boldsymbol{A} = A + \sum_{r=1}^{n}\sum_{i_{1},\dots,i_{r},\;\forall\; 0\le i_{j} \le n} A^{i_{1},\dots,i_{r}}e_{i_{1}}e_{i_{2}}\dots e_{r} + \end{equation*} + + +The critical operation in setting up the geometric algebra is reducing +the geometric product of any two bases to a linear combination of bases so that +we can calculate a multiplication table for the bases. Since the geometric +product is associative we can use the operation (by definition for two vectors +:math:`a\cdot b \equiv (ab+ba)/2` which is a scalar) + + +.. _eq1: + +.. math:: + :nowrap: + :label: 5.1 + + \begin{equation} + e_{i_{j+1}}e_{i_{j}} = 2e_{i_{j+1}}\cdot e_{i_{j}} - e_{i_{j}}e_{i_{j+1}} + \end{equation} + + +These processes are repeated untill every basis list in :math:`\boldsymbol{A}` is in normal +(ascending) order with no repeated elements. As an example consider the +following + +.. math:: + :nowrap: + + \begin{align*} + e_{3}e_{2}e_{1} &= (2(e_{2}\cdot e_{3}) - e_{2}e_{3})e_{1} \\ + &= 2(e_{2}\cdot e_{3})e_{1} - e_{2}e_{3}e_{1} \\ + &= 2(e_{2}\cdot e_{3})e_{1} - e_{2}(2(e_{1}\cdot e_{3})-e_{1}e_{3}) \\ + &= 2((e_{2}\cdot e_{3})e_{1}-(e_{1}\cdot e_{3})e_{2})+e_{2}e_{1}e_{3} \\ + &= 2((e_{2}\cdot e_{3})e_{1}-(e_{1}\cdot e_{3})e_{2}+(e_{1}\cdot e_{2})e_{3})-e_{1}e_{2}e_{3} + \end{align*} + + +which results from repeated application of equation :ref:`5.1 `. If the product of basis vectors contains repeated factors +equation :ref:`5.1 ` can be used to bring the repeated factors next to one another so that if :math:`e_{i_{j}} = e_{i_{j+1}}` +then :math:`e_{i_{j}}e_{i_{j+1}} = e_{i_{j}}\cdot e_{i_{j+1}}` which is a scalar that commutes with all the terms in the product +and can be brought to the front of the product. Since every repeated pair of vectors in a geometric product of :math:`r` factors +reduces the number of noncommutative factors in the product by :math:`r-2`. The number of bases in the multivector algebra is :math:`2^{n}` +and the number containing :math:`r` factors is :math:`{n\choose r}` which is the number of combinations or :math:`n` things +taken :math:`r` at a time (binominal coefficient). + +The other construction required for formulating the geometric algebra is the outer or wedge product (symbol :math:`\wedge`) of :math:`r` +vectors denoted by :math:`a_{1}\wedge\dots\wedge a_{r}`. The wedge product of :math:`r` vectors is called an :math:`r`-blade and is defined +by ([Doran]_,p86) + +.. math:: + :nowrap: + + \begin{equation*} + a_{1}\wedge\dots\wedge a_{r} \equiv \sum_{i_{j_{1}}\dots i_{j_{r}}} \epsilon^{i_{j_{1}}\dots i_{j_{r}}}a_{i_{j_{1}}}\dots a_{i_{j_{1}}} + \end{equation*} + + +where :math:`\epsilon^{i_{j_{1}}\dots i_{j_{r}}}` is the contravariant permutation symbol which is :math:`+1` for an even permutation of the +superscripts, :math:`0` if any superscripts are repeated, and :math:`-1` for an odd permutation of the superscripts. From the definition +:math:`a_{1}\wedge\dots\wedge a_{r}` is antisymmetric in all its arguments and the following relation for the wedge product of a vector :math:`a` and an +:math:`r`-blade :math:`B_{r}` can be derived + +.. _eq2: + +.. math:: + :label: 5.2 + :nowrap: + + \begin{equation} + a\wedge B_{r} = (aB_{r}+(-1)^{r}B_{r}a)/2 + \end{equation} + + + +Using equation :ref:`5.2 ` one can represent the wedge product of all the basis vectors +in terms of the geometric product of all the basis vectors so that one can solve (the system +of equations is lower diagonal) for the geometric product of all the basis vectors in terms of +the wedge product of all the basis vectors. Thus a general multivector :math:`\boldsymbol{B}` can be +represented as a linear combination of a scalar and the basis blades. + +.. math:: + :nowrap: + + \begin{equation*} + \boldsymbol{B} = B + \sum_{r=1}^{n}\sum_{i_{1},\dots,i_{r},\;\forall\; 0\le i_{j} \le n} B^{i_{1},\dots,i_{r}}e_{i_{1}}\wedge e_{i_{2}}\wedge\dots\wedge e_{r} + \end{equation*} + + +Using the blades :math:`e_{i_{1}}\wedge e_{i_{2}}\wedge\dots\wedge e_{r}` creates a graded +algebra where :math:`r` is the grade of the basis blades. The grade-:math:`r` +part of :math:`\boldsymbol{B}` is the linear combination of all terms with +grade :math:`r` basis blades. The scalar part of :math:`\boldsymbol{B}` is defined to +be grade-:math:`0`. Now that the blade expansion of :math:`\boldsymbol{B}` is defined +we can also define the grade projection operator :math:`\left < {\boldsymbol{B}} \right >_{r}` by + +.. math:: + :nowrap: + + \begin{equation*} + \left < {\boldsymbol{B}} \right >_{r} = \sum_{i_{1},\dots,i_{r},\;\forall\; 0\le i_{j} \le n} B^{i_{1},\dots,i_{r}}e_{i_{1}}\wedge e_{i_{2}}\wedge\dots\wedge e_{r} + \end{equation*} + + +and + +.. math:: + :nowrap: + + \begin{equation*} + \left < {\boldsymbol{B}} \right >_{} \equiv \left < {\boldsymbol{B}} \right >_{0} = B + \end{equation*} + +Then if :math:`\boldsymbol{A}_{r}` is an :math:`r`-grade multivector and :math:`\boldsymbol{B}_{s}` is an :math:`s`-grade multivector we have + +.. math:: + :nowrap: + + \begin{equation*} + \boldsymbol{A}_{r}\boldsymbol{B}_{s} = \left < {\boldsymbol{A}_{r}\boldsymbol{B}_{s}} \right >_{\left |{{r-s}}\right |}+\left < {\boldsymbol{A}_{r}\boldsymbol{B}_{s}} \right >_{\left |{{r-s}}\right |+2}+\cdots + \left < {\boldsymbol{A}_{r}\boldsymbol{B}_{s}} \right >_{r+s} + \end{equation*} + + +and define ([Hestenes]_,p6) + + +.. math:: + :nowrap: + + \begin{align*} + \boldsymbol{A}_{r}\wedge\boldsymbol{B}_{s} &\equiv \left < {\boldsymbol{A}_{r}\boldsymbol{B}_{s}} \right >_{r+s} \\ + \boldsymbol{A}_{r}\cdot\boldsymbol{B}_{s} &\equiv \left \{ \begin{array}{cc} + r\mbox{ or }s \ne 0: & \left < {\boldsymbol{A}_{r}\boldsymbol{B}_{s}} \right >_{\left |{{r-s}}\right |} \\ + r\mbox{ or }s = 0: & 0 \end{array} \right \} + \end{align*} + + +where :math:`\boldsymbol{A}_{r}\cdot\boldsymbol{B}_{s}` is called the dot or inner product of +two pure grade multivectors. For the case of two non-pure grade multivectors + + .. math:: + :nowrap: + + \begin{align*} + \boldsymbol{A}\wedge\boldsymbol{B} &= \sum_{r,s}\left < {\boldsymbol{A}} \right >_{r}\wedge\left < {\boldsymbol{B}} \right >_{s} \\ + \boldsymbol{A}\cdot\boldsymbol{B} &= \sum_{r,s\ne 0}\left < {\boldsymbol{A}} \right >_{r}\cdot\left < {\boldsymbol{B}} \right >_{s} + \end{align*} + + +Two other products, the right (:math:`\rfloor`) and left (:math:`\lfloor`) contractions, are defined by + + .. math:: + :nowrap: + + \begin{align*} + \boldsymbol{A}\lfloor\boldsymbol{B} &\equiv \sum_{r,s}\left \{ \begin{array}{cc} \left < {\boldsymbol{A}_r\boldsymbol{B}_{s}} \right >_{r-s} & r \ge s \\ + 0 & r < s \end{array}\right \} \\ + \boldsymbol{A}\rfloor\boldsymbol{B} &\equiv \sum_{r,s}\left \{ \begin{array}{cc} \left < {\boldsymbol{A}_{r}\boldsymbol{B}_{s}} \right >_{s-r} & s \ge r \\ + 0 & s < r\end{array}\right \} + \end{align*} + + +A final operation for multivectors is the reverse. If a multivector :math:`\boldsymbol{A}` is the geometric product of :math:`r` vectors (versor) +so that :math:`\boldsymbol{A} = a_{1}\dots a_{r}` the reverse is defined by + + .. math:: + :nowrap: + + \begin{align*} + \boldsymbol{A}^{\dagger} \equiv a_{r}\dots a_{1} + \end{align*} + + +where for a general multivector we have (the the sum of the reverse of versors) + +.. math:: + :nowrap: + + \begin{equation*} + \boldsymbol{A}^{\dagger} = A + \sum_{r=1}^{n}(-1)^{r(r-1)/2}\sum_{i_{1},\dots,i_{r},\;\forall\; 0\le i_{j} \le n} A^{i_{1},\dots,i_{r}}e_{i_{1}}\wedge e_{i_{2}}\wedge\dots\wedge e_{r} + \end{equation*} + + +note that if :math:`\boldsymbol{A}` is a versor then :math:`\boldsymbol{A}\boldsymbol{A}^{\dagger}\in\Re` and (:math:`AA^{\dagger} \ne 0`) + +.. math:: + :nowrap: + + \begin{equation*} + \boldsymbol{A}^{-1} = {\displaystyle\frac{\boldsymbol{A}^{\dagger}}{\boldsymbol{AA}^{\dagger}}} + \end{equation*} + + +Representation of Multivectors in Sympy +======================================= + +The sympy python module offers a simple way of representing multivectors using linear +combinations of commutative expressions (expressions consisting only of commuting sympy objects) +and noncommutative symbols. We start by defining :math:`n` noncommutative sympy symbols + +.. code-block:: python + + (e_1,...,e_n) = symbols('e_1,...,e_n',commutative=False) + + +Several software packages for numerical geometric algebra calculations are +available from Doran-Lasenby group and the Dorst group. Symbolic packages for +Clifford algebra using orthongonal bases such as +:math:`e_{i}e_{j}+e_{j}e_{i} = 2\eta_{ij}`, where :math:`\eta_{ij}` is a numeric +array are available in Maple and Mathematica. The symbolic algebra module, +*galgebra*, developed for python does not depend on an orthogonal basis +representation, but rather is generated from a set of :math:`n` arbitrary +symbolic vectors, :math:`e_{1},e_{2},\dots,e_{n}` and a symbolic metric +tensor :math:`g_{ij} = e_{i}\cdot e_{j}`. + +In order not to reinvent the wheel all scalar symbolic algebra is handled by the +python module :mod:`sympy` and the abstract basis vectors are encoded as +noncommuting sympy symbols. + +The basic geometic algebra operations will be implemented in python by defining +a multivector class, MV, and overloading the python operators in Table +:ref:`5.1 ` where *A* and *B* are any two multivectors (In the case of +*+*, *-*, *\**, *^*, and *|* the operation is also defined if *A* or +*B* is a sympy symbol or a sympy real number). + + + .. _table1: + + .. csv-table:: + :header: Operation,Result + :widths: 10, 40 + + ''A+B'', sum of multivectors + ''A-B'', difference of multivectors + ''A*B'', geometric product + ''A^B'', outer product of multivectors + ''A|B'', inner product of multivectors + ''AB'', right contraction of multivectors + + Table :ref:`5.1 `. Multivector operations for *galgebra* + + +Since *<* and *>* have no r-forms (in python for the *<* and *>* operators there are no *__rlt__()* and *__rlt__()* member functions to overload) +we can only have mixed modes (scalars and multivectors) if the first operand is a multivector. + +.. note:: + + Except for *<* and *>* all the multivector operators have r-forms so that as long as one of the + operands, left or right, is a multivector the other can be a multivector or a scalar (sympy symbol or integer). + +.. warning:: + + Note that the operator order precedence is determined by python and is not + necessarily that used by geometric algebra. It is **absolutely essential** to + use parenthesis in multivector + expressions containing *^*, *|*, *<*, and/or *>*. As an example let + *A* and *B* be any two multivectors. Then *A + A*B = A +(A*B)*, but + *A+A^B = (2*A)^B* since in python the *^* operator has a lower precedence + than the '+' operator. In geometric algebra the outer and inner products and + the left and right contractions have a higher precedence than the geometric + product and the geometric product has a higher precedence than addition and + subtraction. In python the *^*, *|*, *<*, and *>* all have a lower + precedence than *+* and *-* while *\** has a higher precedence than + *+* and *-*. + +For those users who wish to define a default operator precedence the functions +*define_precedence()* and *GAeval()* are available in the module *galgebra/precedence*. + +.. function:: sympy.galgebra.precedence.define_precedence(gd, op_ord='<>|,^,\*') + + Define the precedence of the multivector operations. The function + *define_precedence()* must be called from the main program and the + first argument *gd* must be set to *globals()*. The second argument + *op_ord* determines the operator precedence for expressions input to + the function *GAeval()*. The default value of *op_ord* is *'<>|,^,\*'*. + For the default value the *<*, *>*, and *|* operations have equal + precedence followed by *^*, and *^* is followed by *\**. + +.. function:: sympy.galgebra.precedence.GAeval(s, pstr=False) + + The function *GAeval()* returns a multivector expression defined by the + string *s* where the operations in the string are parsed according to + the precedences defined by *define_precedence()*. *pstr* is a flag + to print the input and output of *GAeval()* for debugging purposes. + *GAeval()* works by adding parenthesis to the input string *s* with the + precedence defined by *op_ord='<>|,^,\*'*. Then the parsed string is + converted to a sympy expression using the python *eval()* function. + For example consider where *X*, *Y*, *Z*, and *W* are multivectors + + .. code-block:: python + + define_precedence(globals()) + V = GAeval('X|Y^Z*W') + + The sympy variable *V* would evaluate to *((X|Y)^Z)\*W*. + +.. _vbm: + +Vector Basis and Metric +======================= + +The two structures that define the :class:`MV` (multivector) class are the +symbolic basis vectors and the symbolic metric. The symbolic basis +vectors are input as a string with the symbol name separated by spaces. For +example if we are calculating the geometric algebra of a system with three +vectors that we wish to denote as *a0*, *a1*, and *a2* we would define the +string variable: + +.. code-block:: python + + basis = 'a0 a1 a2' + +that would be input into the multivector setup function. The next step would be +to define the symbolic metric for the geometric algebra of the basis we +have defined. The default metric is the most general and is the matrix of +the following symbols + +.. _eq3: + +.. math:: + :nowrap: + :label: 3 + + \begin{equation} + g = \left [ + \begin{array}{ccc} + (a0.a0) & (a0.a1) & (a0.a2) \\ + (a0.a1) & (a1.a1) & (a1.a2) \\ + (a0.a2) & (a1.a2) & (a2.a2) \\ + \end{array} + \right ] + \end{equation} + + +where each of the :math:`g_{ij}` is a symbol representing all of the dot +products of the basis vectors. Note that the symbols are named so that +:math:`g_{ij} = g_{ji}` since for the symbol function +:math:`(a0.a1) \ne (a1.a0)`. + +Note that the strings shown in equation :ref:`5.3 ` are only used when the values +of :math:`g_{ij}` are output (printed). In the *galgebra* module (library) +the :math:`g_{ij}` symbols are stored in a static member of the multivector +class :class:`MV` as the sympy matrix *MV.metric* (:math:`g_{ij}` = *MV.metric[i,j]*). + +The default definition of :math:`g` can be overwritten by specifying a string +that will define :math:`g`. As an example consider a symbolic representation +for conformal geometry. Define for a basis + +.. code-block:: python + + basis = 'a0 a1 a2 n nbar' + +and for a metric + +.. code-block:: python + + metric = '# # # 0 0, # # # 0 0, # # # 0 0, 0 0 0 0 2, 0 0 0 2 0' + +then calling *MV.setup(basis,metric)* would initialize the metric tensor + +.. math:: + :nowrap: + + \begin{equation*} + g = \left [ + \begin{array}{ccccc} + (a0.a0) & (a0.a1) & (a0.a2) & 0 & 0\\ + (a0.a1) & (a1.a1) & (a1.a2) & 0 & 0\\ + (a0.a2) & (a1.a2) & (a2.a2) & 0 & 0 \\ + 0 & 0 & 0 & 0 & 2 \\ + 0 & 0 & 0 & 2 & 0 + \end{array} + \right ] + \end{equation*} + + +Here we have specified that *n* and *nbar* are orthonal to all the +*a*'s, *(n.n) = (nbar.nbar) = 0*, and *(n.nbar) = 2*. Using +*#* in the metric definition string just tells the program to use the +default symbol for that value. + +When *MV.setup* is called multivector representations of the basis local to +the program are instantiated. For our first example that means that the +symbolic vectors named *a0*, *a1*, and *a2* are created and returned from +*MV.setup* via a tuple as in - + +.. code-block:: python + + (a_1,a_2,a3) = MV.setup('a_1 a_2 a_3',metric=metric) + +Note that the python variable name for a basis vector does not have to +correspond to the name give in *MV.setup()*, one may wish to use a +shorted python variable name to reduce programming (typing) errors, for +example one could use - + +.. code-block:: python + + (a1,a2,a3) = MV.setup('a_1 a_2 a_3',metric=metric) + +or + +.. code-block:: python + + (g1,g2,g3) = MV.setup('gamma_1 gamma_2 gamma_3',metric=metric) + +so that if the latex printer is used *e1* would print as :math:`\boldsymbol{e_{1}}` +and *g1* as :math:`\boldsymbol{\gamma_{1}}`. + +.. note:: + + Additionally *MV.setup* has simpified options for naming a set of basis vectors and for + inputing an othogonal basis. + + If one wishes to name the basis vectors :math:`\boldsymbol{e}_{x}`, :math:`\boldsymbol{e}_{y}`, and + :math:`\boldsymbol{e}_{z}` then set *basis='e*x|y|z'* or to name :math:`\boldsymbol{\gamma}_{t}`, + :math:`\boldsymbol{\gamma}_{x}`, :math:`\boldsymbol{\gamma}_{y}`, and :math:`\boldsymbol{\gamma}_{z}` then set + *basis='gamma*t|x|y|z'*. + + For the case of an othogonal basis if the signature of the + vector space is :math:`(1,1,1)` (Euclidian 3-space) set *metric='[1,1,1]'* or if it + is :math:`(1,-1,-1,-1)` (Minkowsi 4-space) set *metric='[1,-1,-1,-1]'*. + + +Representation and Reduction of Multivector Bases +================================================= + +In our symbolic geometric algebra all multivectors +can be obtained from the symbolic basis vectors we have input, via the +different operations available to geometric algebra. The first problem we have +is representing the general multivector in terms terms of the basis vectors. To +do this we form the ordered geometric products of the basis vectors and develop +an internal representation of these products in terms of python classes. The +ordered geometric products are all multivectors of the form +:math:`a_{i_{1}}a_{i_{2}}\dots a_{i_{r}}` where :math:`i_{1}`. + +.. _table3: + +:: + + 1 = 1 + a0 = a0 + a1 = a1 + a2 = a2 + a0^a1 = {-(a0.a1)}1+a0a1 + a0^a2 = {-(a0.a2)}1+a0a2 + a1^a2 = {-(a1.a2)}1+a1a2 + a0^a1^a2 = {-(a1.a2)}a0+{(a0.a2)}a1+{-(a0.a1)}a2+a0a1a2 + +Table :ref:`5.3 `. Bases blades in terms of bases. + +The important thing to notice about Table :ref:`5.3 ` is that it is a +triagonal (lower triangular) system of equations so that using a simple back +substitution algorithm we can solve for the pseudo bases in terms of the blades +giving Table :ref:`5.4 `. + +.. _table4: + +:: + + 1 = 1 + a0 = a0 + a1 = a1 + a2 = a2 + a0a1 = {(a0.a1)}1+a0^a1 + a0a2 = {(a0.a2)}1+a0^a2 + a1a2 = {(a1.a2)}1+a1^a2 + a0a1a2 = {(a1.a2)}a0+{-(a0.a2)}a1+{(a0.a1)}a2+a0^a1^a2 + +Table :ref:`5.4 `. Bases in terms of basis blades. + +Using Table :ref:`5.4 ` and simple substitution we can convert from a base +multivector representation to a blade representation. Likewise, using Table +:ref:`5.3 ` we can convert from blades to bases. + +Using the blade representation it becomes simple to program functions that will +calculate the grade projection, reverse, even, and odd multivector functions. + +Note that in the multivector class *MV* there is a class variable for each +instantiation, *self.bladeflg*, that is set to *False* for a base representation +and *True* for a blade representation. One needs to keep track of which +representation is in use since various multivector operations require conversion +from one representation to the other. + +.. warning:: + + When the geometric product of two multivectors is calculated the module looks to + see if either multivector is in blade representation. If either is the result of + the geometric product is converted to a blade representation. One result of this + is that if either of the multivectors is a simple vector (which is automatically a + blade) the result will be in a blade representation. If *a* and *b* are vectors + then the result *a*b* will be *(a.b)+a^b* or simply *a^b* if *(a.b) = 0*. + + +Outer and Inner Products, Left and Right Contractions +===================================================== + +In geometric algebra any general multivector :math:`A` can be decomposed into +pure grade multivectors (a linear combination of blades of all the same order) +so that in a :math:`n`-dimensional vector space + +.. math:: + :nowrap: + + \begin{equation*} + A = \sum_{r = 0}^{n}A_{r} + \end{equation*} + + +The geometric product of two pure grade multivectors :math:`A_{r}` and +:math:`B_{s}` has the form + +.. math:: + :nowrap: + + \begin{equation*} + A_{r}B_{s} = \left < {A_{r}B_{s}} \right >_{\left |{{r-s}}\right |}+\left < {A_{r}B_{s}} \right >_{\left |{{r-s}}\right |+2}+\cdots+\left < {A_{r}B_{s}} \right >_{r+s} + \end{equation*} + + +where :math:`\left < { } \right >_{t}` projects the :math:`t` grade components of the +multivector argument. The inner and outer products of :math:`A_{r}` and +:math:`B_{s}` are then defined to be + +.. math:: + :nowrap: + + \begin{equation*} + A_{r}\cdot B_{s} = \left < {A_{r}B_{s}} \right >_{\left |{{r-s}}\right |} + \end{equation*} + + +.. math:: + :nowrap: + + \begin{equation*} + A_{r}\wedge B_{s} = \left < {A_{r}B_{s}} \right >_{r+s} + \end{equation*} + + +and + +.. math:: + :nowrap: + + \begin{equation*} + A\cdot B = \sum_{r,s}A_{r}\cdot B_{s} + \end{equation*} + + + +.. math:: + :nowrap: + + \begin{equation*} + A\wedge B = \sum_{r,s}A_{r}\wedge B_{s} + \end{equation*} + + +Likewise the right (:math:`\lfloor`) and left (:math:`\rfloor`) contractions are defined as + + +.. math:: + :nowrap: + + \begin{equation*} + A_{r}\lfloor B_{s} = \left \{ \begin{array}{cc} + \left < {A_{r}B_{s}} \right >_{r-s} & r \ge s \\ + 0 & r < s \end{array} \right \} + \end{equation*} + + +.. math:: + :nowrap: + + \begin{equation*} + A_{r}\rfloor B_{s} = \left \{ \begin{array}{cc} + \left < {A_{r}B_{s}} \right >_{s-r} & s \ge r \\ + 0 & s < r \end{array} \right \} + \end{equation*} + + +and + +.. math:: + :nowrap: + + \begin{equation*} + A\lfloor B = \sum_{r,s}A_{r}\lfloor B_{s} + \end{equation*} + + +.. math:: + :nowrap: + + \begin{equation*} + A\rfloor B = \sum_{r,s}A_{r}\rfloor B_{s} + \end{equation*} + + + + +.. warning:: + + In the *MV* class we have overloaded the *^* operator to represent the outer + product so that instead of calling the outer product function we can write *mv1^ mv2*. + Due to the precedence rules for python it is **absolutely essential** to enclose outer products + in parenthesis. + +.. warning:: + + In the *MV* class we have overloaded the *|* operator for the inner product, + *>* operator for the right contraction, and *<* operator for the left contraction. + Instead of calling the inner product function we can write *mv1|mv2*, *mv1>mv2*, or + *mv1_{i}}^{\dagger} + \end{equation*} + + +but + +.. _eq4: + +.. math:: + :label: 5.4 + :nowrap: + + \begin{equation} + {\left < {A} \right >_{i}}^{\dagger} = \left ( -1\right )^{\frac{i\left ( i-1\right )}{2}}\left < {A} \right >_{i} + \end{equation} + + +which is proved by expanding the blade bases in terms of orthogonal vectors and +showing that equation :ref:`5.4 ` holds for the geometric product of orthogonal +vectors. + +The reverse is important in the theory of rotations in :math:`n`-dimensions. If +:math:`R` is the product of an even number of vectors and :math:`RR^{\dagger} = 1` +then :math:`RaR^{\dagger}` is a composition of rotations of the vector :math:`a`. +If :math:`R` is the product of two vectors then the plane that :math:`R` defines +is the plane of the rotation. That is to say that :math:`RaR^{\dagger}` rotates the +component of :math:`a` that is projected into the plane defined by :math:`a` and +:math:`b` where :math:`R=ab`. :math:`R` may be written +:math:`R = e^{\frac{\theta}{2}U}`, where :math:`\theta` is the angle of rotation +and :math:`u` is a unit blade :math:`\left ( u^{2} = \pm 1\right )` that defines the +plane of rotation. + + +.. _recframe: + +Reciprocal Frames +================= + +If we have :math:`M` linearly independent vectors (a frame), +:math:`a_{1},\dots,a_{M}`, then the reciprocal frame is +:math:`a^{1},\dots,a^{M}` where :math:`a_{i}\cdot a^{j} = \delta_{i}^{j}`, +:math:`\delta_{i}^{j}` is the Kronecker delta (zero if :math:`i \ne j` and one +if :math:`i = j`). The reciprocal frame is constructed as follows: + +.. math:: + :nowrap: + + \begin{equation*} + E_{M} = a_{1}\wedge\dots\wedge a_{M} + \end{equation*} + + + + +.. math:: + :nowrap: + + \begin{equation*} + E_{M}^{-1} = {\displaystyle\frac{E_{M}}{E_{M}^{2}}} + \end{equation*} + + +Then + +.. math:: + :nowrap: + + \begin{equation*} + a^{i} = \left ( -1\right )^{i-1}\left ( a_{1}\wedge\dots\wedge \breve{a}_{i} \wedge\dots\wedge a_{M}\right ) E_{M}^{-1} + \end{equation*} + + +where :math:`\breve{a}_{i}` indicates that :math:`a_{i}` is to be deleted from +the product. In the standard notation if a vector is denoted with a subscript +the reciprocal vector is denoted with a superscript. The multivector setup +function *MV.setup(basis,metric,rframe)* has the argument *rframe* with a +default value of *False*. If it is set to *True* the reciprocal frame of +the basis vectors is calculated. Additionally there is the function +*reciprocal_frame(vlst,names='')* external to the *MV* class that will +calculate the reciprocal frame of a list, *vlst*, of vectors. If the argument +*names* is set to a space delimited string of names for the vectors the +reciprocal vectors will be given these names. + + +.. _deriv: + +Geometric Derivative +==================== + +If :math:`F` is a multivector field that is a function of a vector +:math:`x = x^{i}\boldsymbol{e}_{i}` (we are using the summation convention that +pairs of subscripts and superscripts are summed over the dimension of the vector +space) then the geometric derivative :math:`\nabla F` is given by (in this +section the summation convention is used): + +.. math:: + :nowrap: + + \begin{equation*} + \nabla F = \boldsymbol{e}^{i}{\displaystyle\frac{\partial F}{\partial x^{i}}} + \end{equation*} + + +If :math:`F_{R}` is a grade-:math:`R` multivector and +:math:`F_{R} = F_{R}^{i_{1}\dots i_{R}}\boldsymbol{e}_{i_{1}}\wedge\dots\wedge \boldsymbol{e}_{i_{R}}` +then + +.. math:: + :nowrap: + + \begin{equation*} + \nabla F_{R} = {\displaystyle\frac{\partial F_{R}^{i_{1}\dots i_{R}}}{\partial x^{j}}}\boldsymbol{e}^{j}\left (\boldsymbol{e}_{i_{1}}\wedge + \dots\wedge \boldsymbol{e}_{i_{R}} \right ) + \end{equation*} + + +Note that +:math:`\boldsymbol{e}^{j}\left (\boldsymbol{e}_{i_{1}}\wedge\dots\wedge \boldsymbol{e}_{i_{R}} \right )` +can only contain grades :math:`R-1` and :math:`R+1` so that :math:`\nabla F_{R}` +also can only contain those grades. For a grade-:math:`R` multivector +:math:`F_{R}` the inner (div) and outer (curl) derivatives are defined as + + +.. math:: + :nowrap: + + \begin{equation*} + \nabla\cdot F_{R} = \left < \nabla F_{R}\right >_{R-1} + \end{equation*} + + +and + +.. math:: + :nowrap: + + \begin{equation*} + \nabla\wedge F_{R} = \left < \nabla F_{R}\right >_{R+1} + \end{equation*} + + +For a general multivector function :math:`F` the inner and outer derivatives are +just the sum of the inner and outer dervatives of each grade of the multivector +function. + +Curvilinear coordinates are derived from a vector function +:math:`x(\boldsymbol{\theta})` where +:math:`\boldsymbol{\theta} = \left (\theta_{1},\dots,\theta_{N}\right )` where the number of +coordinates is equal to the dimension of the vector space. In the case of +3-dimensional spherical coordinates :math:`\boldsymbol{\theta} = \left ( r,\theta,\phi \right )` +and the coordinate generating function :math:`x(\boldsymbol{\theta})` is + +.. math:: + :nowrap: + + \begin{equation*} + x = r \cos\left({\phi}\right) \sin\left({\theta}\right){\boldsymbol{{e}_{x}}}+ r \sin\left({\phi}\right) \sin\left({\theta}\right){\boldsymbol{{e}_{y}}}+ r \cos\left({\theta}\right){\boldsymbol{{e}_{z}}} + \end{equation*} + + +A coordinate frame is derived from :math:`x` by +:math:`\boldsymbol{e}_{i} = {\displaystyle\frac{\partial {x}}{\partial {\theta^{i}}}}`. The following show the frame for +spherical coordinates. + +.. math:: + :nowrap: + + \begin{equation*} + \boldsymbol{e}_{r} = \cos\left({\phi}\right) \sin\left({\theta}\right){\boldsymbol{{e}_{x}}}+\sin\left({\phi}\right) \sin\left({\theta}\right){\boldsymbol{{e}_{y}}}+\cos\left({\theta}\right){\boldsymbol{{e}_{z}}} + \end{equation*} + + + + +.. math:: + :nowrap: + + \begin{equation*} + \boldsymbol{e}_{{\theta}} = \cos\left({\phi}\right) \cos\left({\theta}\right){\boldsymbol{{e}_{x}}}+r \cos\left({\theta}\right) \sin\left({\phi}\right){\boldsymbol{{e}_{y}}} - r \sin\left({\theta}\right){\boldsymbol{{e}_{z}}} + \end{equation*} + + + + +.. math:: + :nowrap: + + \begin{equation*} + \boldsymbol{e}_{{\phi}} = - r \sin\left({\phi}\right) \sin\left({\theta}\right){\boldsymbol{{e}_{x}}}+r \cos\left({\phi}\right) \sin\left({\theta}\right){\boldsymbol{{e}_{y}}} + \end{equation*} + + +The coordinate frame generated in this manner is not necessarily normalized so +define a normalized frame by + +.. math:: + :nowrap: + + \begin{equation*} + \boldsymbol{\hat{e}}_{i} = {\displaystyle\frac{\boldsymbol{e}_{i}}{\sqrt{\left |{{\boldsymbol{e}_{i}^{2}}}\right |}}} = {\displaystyle\frac{\boldsymbol{e}_{i}}{\left |{{\boldsymbol{e}_{i}}}\right |}} + \end{equation*} + + +This works for all :math:`\boldsymbol{e}_{i}^{2} \neq 0` since we have defined +:math:`\left |\boldsymbol{e}_{i}\right | = \sqrt{\left |\boldsymbol{e}_{i}^{2}\right |}`. For spherical +coordinates the normalized frame vectors are + +.. math:: + :nowrap: + + \begin{equation*} + \boldsymbol{\hat{e}}_{r} = \cos\left({\phi}\right) \sin\left({\theta}\right){\boldsymbol{{e}_{x}}}+\sin\left({\phi}\right) \sin\left({\theta}\right){\boldsymbol{{e}_{y}}}+\cos\left({\theta}\right){\boldsymbol{{e}_{z}}} + \end{equation*} + + + + +.. math:: + :nowrap: + + \begin{equation*} + \boldsymbol{\hat{e}}_{{\theta}} = \cos\left({\phi}\right) \cos\left({\theta}\right){\boldsymbol{{e}_{x}}}+\cos\left({\theta}\right) \sin\left({\phi}\right){\boldsymbol{{e}_{y}}}- \sin\left({\theta}\right){\boldsymbol{{e}_{z}}} + \end{equation*} + + + + +.. math:: + :nowrap: + + \begin{equation*} + \boldsymbol{\hat{e}}_{{\phi}} = - \sin\left({\phi}\right){\boldsymbol{{e}_{x}}}+\cos\left({\phi}\right){\boldsymbol{{e}_{y}}} + \end{equation*} + + +The geometric derivative in curvilinear coordinates is given by + +.. math:: + :nowrap: + + \begin{align*} + \nabla F_{R} & = \boldsymbol{e}^{i}{\displaystyle\frac{\partial {}}{\partial {x^{i}}}}\left ( F_{R}^{i_{1}\dots i_{R}} + \boldsymbol{\hat{e}}_{i_{1}}\wedge\dots\wedge\boldsymbol{\hat{e}}_{i_{R}}\right ) \\ + & = \boldsymbol{e^{j}}{\displaystyle\frac{\partial {}}{\partial {\theta^{j}}}}\left ( F_{R}^{i_{1}\dots i_{R}} + \boldsymbol{\hat{e}}_{i_{1}}\wedge\dots\wedge\boldsymbol{\hat{e}}_{i_{R}}\right ) \\ + & = \left ({\displaystyle\frac{\partial {}}{\partial {\theta^{j}}}} F_{R}^{i_{1}\dots i_{R}}\right ) + \boldsymbol{e^{j}}\left (\boldsymbol{\hat{e}}_{i_{1}}\wedge\dots\wedge\boldsymbol{\hat{e}}_{i_{R}}\right )+ + F_{R}^{i_{1}\dots i_{R}}\boldsymbol{e^{j}} + {\displaystyle\frac{\partial {}}{\partial {\theta^{j}}}}\left (\boldsymbol{\hat{e}}_{i_{1}}\wedge\dots\wedge\boldsymbol{\hat{e}}_{i_{R}}\right ) \\ + & = \left ({\displaystyle\frac{\partial {}}{\partial {\theta^{j}}}} F_{R}^{i_{1}\dots i_{R}}\right ) + \boldsymbol{e^{j}}\left (\boldsymbol{\hat{e}}_{i_{1}}\wedge\dots\wedge\boldsymbol{\hat{e}}_{i_{R}}\right )+ + F_{R}^{i_{1}\dots i_{R}}C\left \{ \boldsymbol{\hat{e}}_{i_{1}}\wedge\dots\wedge\boldsymbol{\hat{e}}_{i_{R}}\right \} + \end{align*} + + +where + +.. math:: + :nowrap: + + \begin{equation*} + C\left \{ \boldsymbol{\hat{e}}_{i_{1}}\wedge\dots\wedge\boldsymbol{\hat{e}}_{i_{R}}\right \} = \boldsymbol{e^{j}}{\displaystyle\frac{\partial {}}{\partial {\theta^{j}}}} + \left (\boldsymbol{\hat{e}}_{i_{1}}\wedge\dots\wedge\boldsymbol{\hat{e}}_{i_{R}}\right ) + \end{equation*} + + +are the connection multivectors for the curvilinear coordinate system. For a +spherical coordinate system they are + +.. math:: + :nowrap: + + \begin{equation*} + C\left \{\boldsymbol{\hat{e}}_{r}\right \} = \frac{2}{r} + \end{equation*} + + + + +.. math:: + :nowrap: + + \begin{equation*} + C\left \{\boldsymbol{\hat{e}}_{\theta}\right \} = \frac{\cos\left({\theta}\right)}{r \sin\left({\theta}\right)} + +\frac{1}{r}\boldsymbol{\hat{e}}_{r}\wedge\boldsymbol{\hat{e}}_{\theta} + \end{equation*} + + + + +.. math:: + :nowrap: + + \begin{equation*} + C\left \{\boldsymbol{\hat{e}}_{\phi}\right \} = \frac{1}{r}\boldsymbol{{\hat{e}}_{r}}\wedge\boldsymbol{\hat{e}}_{{\phi}}+ \frac{\cos\left({\theta}\right)}{r \sin\left({\theta}\right)}\boldsymbol{\hat{e}}_{{\theta}}\wedge\boldsymbol{\hat{e}}_{{\phi}} + \end{equation*} + + + + +.. math:: + :nowrap: + + \begin{equation*} + C\left \{\hat{e}_{r}\wedge\hat{e}_{\theta}\right \} = - \frac{\cos\left({\theta}\right)}{r \sin\left({\theta}\right)} + \boldsymbol{\hat{e}}_{r}+\frac{1}{r}\boldsymbol{\hat{e}}_{{\theta}} + \end{equation*} + + + + +.. math:: + :nowrap: + + \begin{equation*} + C\left \{\boldsymbol{\hat{e}}_{r}\wedge\boldsymbol{\hat{e}}_{\phi}\right \} = \frac{1}{r}\boldsymbol{\hat{e}}_{{\phi}} + - \frac{\cos\left({\theta}\right)}{r \sin\left({\theta}\right)}\boldsymbol{\hat{e}}_{r}\wedge\boldsymbol{\hat{e}}_{{\theta}}\wedge\boldsymbol{\hat{e}}_{{\phi}} + \end{equation*} + + + + +.. math:: + :nowrap: + + \begin{equation*} + C\left \{\boldsymbol{\hat{e}}_{\theta}\wedge\boldsymbol{\hat{e}}_{\phi}\right \} = \frac{2}{r}\boldsymbol{\hat{e}}_{r}\wedge + \boldsymbol{\hat{e}}_{\theta}\wedge\boldsymbol{\hat{e}}_{\phi} + \end{equation*} + + + + +.. math:: + :nowrap: + + \begin{equation*} + C\left \{\boldsymbol{\hat{e}}_r\wedge\boldsymbol{\hat{e}}_{\theta}\wedge\boldsymbol{\hat{e}}_{\phi}\right \} = 0 + \end{equation*} + + +Numpy, LaTeX, and Ansicon Installation +====================================== + +To install the geometric algebra module on windows,linux, or OSX perform the following operations + + #. Install sympy. *galgebra* is included in sympy. + + #. To install texlive in linux or windows + + #. Go to and click on "install-tl.zip" o download + #. Unzip "install-tl.zip" anywhere on your machine + #. Open the file "readme.en.html" in the "readme-html.dir" directory. This file contains the information needed to install texlive. + #. Open a terminal (console) in the "install-tl-XXXXXX" directory + #. Follow the instructions in "readme.en.html" file to run the install-tl.bat file in windows or the install-tl script file in linux. + + #. For OSX install mactex from . + + #. Install python-nympy if you want to calculate numerical matrix functons (determinant, inverse, eigenvalues, etc.). + For windows go to and install the distribution of numpy + appropriate for your system. For OSX go to . + #. It is strongly suggested that you go to and install the version of the "geany" editor appropriate for your system. + #. If you wish to use "enhance_print" on windows - + + #. Go to and download "ansicon" + #. In the Edit -> Preferences -> Tools menu of "geany" enter into the Terminal input the full path of "ansicon.exe" + +After installation if you are doing you code development in the *galgebra* directory you need only include + +.. code-block:: python + + from sympy.galgebra.printing import xdvi,enhance_print + from sympy.galgebra.ga import * + +to use the *galgebra* module. + +In addition to the code shown in the examples section of this document there are more examples in the Examples directory under the +*galgebra* directory. + +Module Components +================= + + +Initializing Multivector Class +------------------------------ + +The multivector class is initialized with: + + +.. function:: sympy.galgebra.ga.MV.setup(basis, metric=None, coords=None, rframe=False, debug=False, curv=(None, None)) + + The *basis* and *metric* parameters were described in section :ref:`vbm`. If + *rframe=True* the reciprocal frame of the symbolic bases vectors is calculated. + If *debug=True* the data structure required to initialize the :class:`MV` class + are printer out. *coords* is a tuple of :class:`sympy` symbols equal in length to + the number of basis vectors. These symbols are used as the arguments of a + multivector field as a function of position and for calculating the derivatives + of a multivector field (if *coords* is defined then *rframe* is automatically + set equal to *True*). Additionally, :func:`MV.setup` calculates the pseudo scalar, + :math:`I` and makes them available to the programmer as *MV.I* and *MV.Iinv*. + + :func:`MV.setup` always returns a tuple containing the basis vectors (as multivectors) + so that if we have the code + + .. code-block:: python + + (e1,e2,e3) = MV.setup('e_1 e_2 e_3') + + then we can define a multivector by the expression + + .. code-block:: python + + (a1,a2,a3) = symbols('a__1 a__2 a__3') + A = a1*e1+a2*e2+a3*e3 + + Another option is + + .. code-block:: python + + (e1,e2,e3) = MV.setup('e*1|2|3') + + which produce the same results as the previous method. Note that if + we had used + + .. code-block:: python + + (e1,e2,e3) = MV.setup('e*x|y|z') + + then the basis vectors would have been labeled *e_x*, *e_y*, and *e_z*. If + *coords* is defined then :func:`MV.setup` returns the tuple + + .. code-block:: python + + X = (x,y.z) = symbols('x y z') + (ex,ey,ez,grad) = MV.setup('e',coords=X) + + the basis vectros are again labeled *e_x*, *e_y*, and *e_z* and the + additional vector *grad* is returned. *grad* acts as the gradient + operator (geometric derivative) so that if :func:`F` is a multivector + function of *(x,y,z)* then + + .. code-block:: python + + DFl = grad*F + DFr = F*grad + + are the left and right geometric derivatives of :func:`F`. + + The final parameter in :func:`MV.setup` is *curv* which defines a + curvilinear coordinate system. If 3-dimensional spherical coordinates + are required we would define - + + .. code-block:: python + + X = (r,th,phi) = symbols('r theta phi') + curv = [[r*cos(phi)*sin(th),r*sin(phi)*sin(th),r*cos(th)],[1,r,r*sin(th)]] + (er,eth,ephi,grad) = MV.setup('e_r e_theta e_phi',metric='[1,1,1]',coords=X,curv=curv) + + The first component of *curv* is + + .. code-block:: python + + [r*cos(phi)*sin(th),r*sin(phi)*sin(th),r*cos(th)] + + This is the position vector for the spherical coordinate system expressed + in terms of the rectangular coordinate components given in terms of the + spherical coordinates *r*, *th*, and *phi*. The second component + of *curv* is + + .. code-block:: python + + [1,r,r*sin(th)] + + The components of *curv[1]* are the normalizing factors for the basis + vectors of the spherical coordinate system that are calculated from the + derivatives of *curv[0]* with respect to the coordinates *r*, *th*, + and *phi*. In theory the normalizing factors can be calculated from + the derivatives of *curv[0]*. In practice one cannot currently specify + in sympy that the square of a function is always positive which leads to + problems when the normalizing factor is the square root of a squared + function. To avoid these problems the normalizing factors are explicitly + defined in *curv[1]*. + + .. note:: + + In the case of curvlinear coordinates *debug* also prints the connection + multivectors. + + +Instantiating a Multivector +--------------------------- + +Now that grades and bases have been described we can show all the ways that a +multivector can be instantiated. As an example assume that the multivector space +is initialized with + + .. code-block:: python + + (e1,e2,e3) = MV.setup('e_1 e_2 e_3') + +then multivectors could be instantiated with + + .. code-block:: python + + (a1,a2,a3) = symbols('a__1 a__2 a__3') + x = a1*e1+a2*e2+a3*e3 + y = x*e1*e2 + z = x|y + w = x^y + +or with the multivector class constructor: + +.. class:: sympy.galgebra.ga.MV(base=None,mvtype=None,fct=False,blade_rep=True) + + *base* is a string that defines the name of the multivector for output + purposes. *base* and *mvtype* are defined by the following table and *fct* is a + switch that will convert the symbolic coefficients of a multivector to functions + if coordinate variables have been defined when :func:`MV.setup` is called: + + + .. list-table:: + :widths: 20, 30, 65 + :header-rows: 1 + + * - mvtype + - base + - result + * - default + - default + - Zero multivector + * - scalar + - string s + - symbolic scalar of value Symbol(s) + * - vector + - string s + - symbolic vector + * - grade2 or bivector + - string s + - symbolic bivector + * - grade + - string s,n + - symbolic n-grade multivector + * - pseudo + - string s + - symbolic pseudoscalar + * - spinor + - string s + - symbolic even multivector + * - mv + - string s + - symbolic general multivector + * - default + - sympy scalar c + - zero grade multivector with coefficient c + * - default + - multivector + - copy constructor for multivector + + + If the *base* argument is a string s then the coefficients of the resulting + multivector are named as follows: + + The grade r coefficients consist of the base string, s, followed by a double + underscore, __, and an index string of r symbols. If *coords* is defined the + index string will consist of coordinate names in a normal order defined by + the *coords* tuple. If *coords* is not defined the index string will be + integers in normal (ascending) order (for an n dimensional vector space the + indices will be 1 to n). The double underscore is used because the latex printer + interprets it as a superscript and superscripts in the coefficients will balance + subscripts in the bases. + + For example if If *coords=(x,y,z)* and the base is *A*, the list of all possible + coefficients for the most general multivector would be *A*, *A__x*, *A__y*, *A__z*, + *A__xy*, *A__xz*, *A__yz*, and *A_xyz*. If the latex printer is used and *e* is the + base for the basis vectors then the pseudo scalar would print as + :math:`A^{xyz}\boldsymbol{e_{x}\wedge e_{y}\wedge e_{z}}`. If coordinates are not defined it would print + as :math:`A^{123}\boldsymbol{e_{1}\wedge e_{2}\wedge e_{3}}`. For printed output all multivectors are represented + in terms of products of the basis vectors, either as geometric products or wedge products. This + is also true for the output of expressions containing reciprocal basis vectors. + + + If the *fct* argument of :func:`MV` is set to *True* and the *coords* argument in + :func:`MV.setup` is defined the symbolic coefficients of the multivector are functions + of the coordinates. + + +Basic Multivector Class Functions +--------------------------------- + +.. function:: sympy.galgebra.ga.MV.convert_to_blades(self) + + Convert multivector from the base representation to the blade representation. + If multivector is already in blade representation nothing is done. + + +.. function:: sympy.galgebra.ga.MV.convert_from_blades(self) + + Convert multivector from the blade representation to the base representation. + If multivector is already in base representation nothing is done. + +.. function:: sympy.galgebra.ga.MV.dd(self, v) + + For a mutivector function *F* and a vector *v* then *F.dd(v)* is the + directional derivate of *F* in the direction *v*, :math:`( v\cdot\nabla ) F`. + +.. function:: sympy.galgebra.ga.MV.diff(self, var) + + Calculate derivative of each multivector coefficient with resepect to + variable *var* and form new multivector from coefficients. + +.. function:: sympy.galgebra.ga.MV.dual(self) + + Return dual of multivector which is multivector left multiplied by + pseudoscalar *MV.I* ([Hestenes]_,p22). + +.. function:: sympy.galgebra.ga.MV.even(self) + + Return the even grade components of the multivector. + +.. function:: sympy.galgebra.ga.MV.exp(self, alpha=1, norm=0, mode='T') + + Return exponential of a blade (if self is not a blade error message + is generated). If :math:`A` is the blade then :math:`e^{\alpha A}` is returned + where the default *mode*, *'T'*, assumes :math:`AA < 0` so that + + .. math:: + :nowrap: + + \begin{equation*} + e^{\alpha A} = {\cos}\left ( {\alpha\sqrt{-A^{2}}} \right )+{\sin}\left ( {\alpha\sqrt{-A^{2}}} \right ){\displaystyle\frac{A}{\sqrt{-A^{2}}}}. + \end{equation*} + + + If the mode is not *'T'* then :math:`AA > 0` is assumed so that + + .. math:: + :nowrap: + + \begin{equation*} + e^{\alpha A} = {\cosh}\left ( {\alpha\sqrt{A^{2}}} \right )+{\sinh}\left ( {\alpha\sqrt{A^{2}}} \right ){\displaystyle\frac{A}{\sqrt{A^{2}}}}. + \end{equation*} + + + If :math:`norm = N > 0` then + + .. math:: + :nowrap: + + \begin{equation*} + e^{\alpha A} = {\cos}\left ( {\alpha N} \right )+{\sin}\left ( {\alpha N} \right ){\displaystyle\frac{A}{N}} + \end{equation*} + + + or + + .. math:: + :nowrap: + + \begin{equation*} + e^{\alpha A} = {\cosh}\left ( {\alpha N} \right )+{\sinh}\left ( {\alpha N} \right ){\displaystyle\frac{A}{N}} + \end{equation*} + + + depending on the value of *mode*. + +.. function:: sympy.galgebra.ga.MV.expand(self) + + Return multivector in which each coefficient has been expanded using + sympy *expand()* function. + +.. function:: sympy.galgebra.ga.MV.factor(self) + + Apply the *sympy* *factor* function to each coefficient of the multivector. + +.. function:: sympy.galgebra.ga.MV.func(self, fct) + + Apply the *sympy* scalar function *fct* to each coefficient of the multivector. + +.. function:: sympy.galgebra.ga.MV.grade(self, igrade=0) + + Return a multivector that consists of the part of the multivector of + grade equal to *igrade*. If the multivector has no *igrade* part + return a zero multivector. + +.. function:: sympy.galgebra.ga.MV.inv(self) + + Return the inverse of the multivector if *self*sefl.rev()* is a nonzero ctor. + +.. function:: sympy.galgebra.ga.MV.norm(self) + + Return the norm of the multvector :math:`M` (*M.norm()*) defined by + :math:`\sqrt{MM^{\dagger}}`. If :math:`MM^{\dagger}` is a scalar (a sympy scalar + is returned). If :math:`MM^{\dagger}` in not a scalar the program exits + with an error message. + +.. function:: sympy.galgebra.ga.MV.norm(self) + + Return the square of norm of the multvector :math:`M` (*M.norm2()*) defined by + :math:`MM^{\dagger}`. If :math:`MM^{\dagger}` is a scalar (a sympy scalar + is returned). If :math:`MM^{\dagger}` in not a scalar the program exits + with an error message. + +.. function:: sympy.galgebra.ga.MV.scalar(self) + + Return the coefficient (sympy scalar) of the scalar part of a + multivector. + +.. function:: sympy.galgebra.ga.MV.simplify(self) + + Return multivector where sympy simplify function has been applied to + each coefficient of the multivector. + +.. function:: sympy.galgebra.ga.MV.subs(self, x) + + Return multivector where sympy subs function has been applied to each + coefficient of multivector for argument dictionary/list x. + +.. function:: sympy.galgebra.ga.MV.rev(self) + + Return the reverse of the multivector. See section :ref:`reverse`. + +.. function:: sympy.galgebra.ga.MV.set_coef(self, grade, base, value) + + Set the multivector coefficient of index *(grade,base)* to *value*. + +.. function:: sympy.galgebra.ga.MV.trigsimp(self, **kwargs) + + Apply the *sympy* trignometric simplification fuction *trigsimp* to + each coefficient of the multivector. *\*\*kwargs* are the arguments of + trigsimp. See *sympy* documentation on *trigsimp* for more information. + +Basic Multivector Functions +--------------------------- + +.. autofunction:: sympy.galgebra.ga.diagpq + +.. autofunction:: sympy.galgebra.ga.arbitrary_metric + +.. autofunction:: sympy.galgebra.ga.arbitrary_metric_conformal + +.. function:: sympy.galgebra.ga.Com(A, B) + + Calulate commutator of multivectors *A* and *B*. Returns :math:`(AB-BA)/2`. + +.. function:: sympy.galgebra.ga.DD(v, f) + + Calculate directional derivative of multivector function *f* in direction of + vector *v*. Returns *f.dd(v)*. + +.. function:: sympy.galgebra.ga.Format(Fmode=True, Dmode=True, ipy=False) + + See latex printing. + +.. function:: sympy.galgebra.precedence.GAeval(s, pstr=False) + + Returns multivector expression for string *s* with operator precedence for + string *s* defined by inputs to function *define_precedence()*. if *pstr=True* + *s* and *s* with parenthesis added to enforce operator precedence are printed. + +.. function:: sympy.galgebra.ga.Nga(x, prec=5) + + If *x* is a multivector with coefficients that contain floating point numbers, *Nga()* + rounds all these numbers to a precision of *prec* and returns the rounded multivector. + +.. function:: sympy.galgebra.ga.ReciprocalFrame(basis, mode='norm') + + If *basis* is a list/tuple of vectors, *ReciprocalFrame()* returns a tuple of reciprocal + vectors. If *mode=norm* the vectors are normalized. If *mode* is anything other than + *norm* the vectors are unnormalized and the normalization coefficient is added to the + end of the tuple. One must divide by the coefficient to normalize the vectors. + +.. function:: sympy.galgebra.ga.ScalarFunction(TheFunction) + + If *TheFuction* is a real *sympy* fuction a scalar multivector function is returned. + +.. function:: sympy.galgebra.ga.cross(M1, M2) + + If *M1* and *M2* are 3-dimensional euclidian vectors the vector cross product is + returned, :math:`v_{1}\times v_{2} = -I\left ( {{v_{1}\wedge v_{2}}} \right )`. + +.. function:: sympy.galgebra.precedence.define_precedence(gd, op_ord='<>|,^,*') + + This is used with the *GAeval()* fuction to evaluate a string representing a multivector + expression with a revised operator precedence. *define_precedence()* redefines the operator + precedence for multivectors. *define_precedence()* must be called in the main program an the + argument *gd* must be *globals()*. The argument *op_ord* defines the order of operator + precedence from high to low with groups of equal precedence separated by commas. the default + precedence *op_ord='<>|,^,\*'* is that used by Hestenes ([Hestenes]_,p7,[Doran]_,p38). + +.. function:: sympy.galgebra.ga.dual(M) + + Return the dual of the multivector *M*, :math:`MI^{-1}`. + +.. function:: sympy.galgebra.ga.inv(B) + + If for the multivector :math:`B`, :math:`BB^{\dagger}` is a nonzero scalar, return :math:`B^{-1} = B^{\dagger}/(BB^{\dagger})`. + +.. function:: sympy.galgebra.ga.proj(B, A) + + Project blade A on blade B returning :math:`\left ( {{A\lfloor B}} \right )B^{-1}`. + +.. function:: sympy.galgebra.ga.refl(B, A) + + Reflect blade *A* in blade *B*. If *r* is grade of *A* and *s* is grade of *B* + returns :math:`(-1)^{s(r+1)}BAB^{-1}`. + +.. function:: sympy.galgebra.ga.rot(itheta, A) + + Rotate blade *A* by 2-blade *itheta*. Is is assumed that *itheta\*itheta > 0* so that + the rotation is Euclidian and not hyperbolic so that the angle of + rotation is *theta = itheta.norm()*. Ther in 3-dimensional Euclidian space. *theta* is the angle of rotation (scalar in radians) and + *n* is the vector axis of rotation. Returned is the rotor *cos(theta)+sin(theta)*N* where *N* is + the normalized dual of *n*. + +Multivector Derivatives +----------------------- + +The various derivatives of a multivector function is accomplished by +multiplying the gradient operator vector with the function. The gradiant +operation vector is returned by the *MV.setup()* function if coordinates +are defined. For example if we have for a 3-D vector space + + .. code-block:: python + + X = (x,y,z) = symbols('x y z') + (ex,ey,ez,grad) = MV.setup('e*x|y|z',metric='[1,1,1]',coords=X) + +Then the gradient operator vector is *grad* (actually the user can give +it any name he wants to). Then the derivatives of the multivector +function *F* are given by + + .. code-block:: python + + F = MV('F','mv',fct=True) + +.. math:: + :nowrap: + + \begin{align*} + \nabla F &= grad*F \\ + F \nabla &= F*grad \\ + \nabla \wedge F &= grad \wedge F \\ + F \wedge \nabla &= F \wedge grad \\ + \nabla \cdot F &= grad|F \\ + F \cdot \nabla F &= F|grad \\ + \nabla \lfloor F &= grad < F \\ + F \lfloor \nabla &= F < grad \\ + \nabla \rfloor F &= grad > F \\ + F \rfloor \nabla &= F > grad + \end{align*} + + + +The preceding code block gives examples of all possible multivector +derivatives of the multivector function *F* where \* give the left and +right geometric derivatives, ^ gives the left and right exterior (curl) +derivatives, | gives the left and right interior (div) derivatives, +< give the left and right derivatives for the left contraction, and +> give the left and right derivatives for the right contraction. To +understand the left and right derivatives see a reference on geometric +calculus ([Doran]_,chapter6). + +If one is taking the derivative of a complex expression that expression +should be in parenthesis. Additionally, whether or not one is taking the +derivative of a complex expression the *grad* vector and the expression +it is operating on should always be in parenthesis unless the grad operator +and the expression it is operating on are the only objects in the expression. + +Vector Manifolds +---------------- + +In addtition to the *galgebra* module there is a *manifold* module that allows +for the definition of a geometric algebra and calculus on a vector manifold. +The vector mainfold is defined by a vector function of some coordinates +in an embedding vector space ([Doran]_,p202,[Hestenes]_,p139). For example the unit 2-sphere would be the +collection of vectors on the unit shpere in 3-dimensions with possible +coordinates of :math:`\theta` and :math:`\phi` the angles of elevation and +azimuth. A vector function :math:`{X}\left ( {\theta,\phi} \right )` that defines the manifold +would be given by + +.. math:: + :nowrap: + + \begin{equation*} + {X}\left ( {\theta,\phi} \right ) = {\cos}\left ( {\theta} \right )\boldsymbol{e_{z}}+{\cos}\left ( {\theta} \right )\left ( {{{\cos}\left ( {\phi} \right )\boldsymbol{e_{x}} + +{\sin}\left ( {\phi} \right )\boldsymbol{e_{y}}}} \right ) + \end{equation*} + + + +The module *manifold.py* is transitionary in that all calculation are performed in the embedding vector space (geometric algebra). +Thus due to the limitations on *sympy*'s *simplify()* and *trigsimp()*, simple expressions may appear to be very complicated since they are expressed +in terms of the basis vectors (bases/blades) of the embedding space and not in terms of the vector space (geometric algebra) formed +from the manifold's basis vectors. A future implementation of *Manifold.py* will correct this difficiency. The member functions of +the vector manifold follow. + +.. function:: Manifold(x, coords, debug=False, I=None) + + Initializer for vector manifold where *x* is the vector function of the *coords* that defines the manifold and *coords* is the list/tuple + of sympy symbols that are the coordinates. The basis vectors of the manifold as a fuction of the coordinates are returned as a tuple. *I* + is the pseudo scalar for the manifold. The default is for the initializer to calculate *I*, however for complicated *x* functions (especially + where trigonometric functions of the coordinates are involved) it is sometimes a good idea to calculate *I* separately and input it to *Manifold()*. + +.. function:: Basis(self) + + Return the basis vectors of the manifold as a tuple. + +.. function:: DD(self, v, F, opstr=False) + + Return the manifold directional derivative of a multivector function *F* defined on the manifold in the vector direction *v*. + +.. function:: Grad(self, F) + + Return the manifold multivector derivative of the multivector function *F* defined on the manifold. + +.. function:: Proj(self, F) + + Return the projection of the multivector *F* onto the manifold tangent space. + + +An example of a simple vector manifold is shown below which demonstrates the instanciation of a manifold, the defining +of vector and scalar functions on the manifold and the calculation of the geometric derivative of those functions. + +.. image:: manifold_testlatex.png + + +Standard Printing +----------------- + +Printing of multivectors is handled by the module *galgebra/printing* which contains +a string printer class, *GA_Printer* derived from the sympy string printer class and a latex +printer class, *GA_LatexPrinter*, derived from the sympy latex printer class. Additionally, there +is an *enhanced_print* class that enhances the console output of sympy to make +the printed output multivectors, functions, and derivatives more readable. +*enhanced_print* requires an ansi console such as is supplied in linux or the +program *ansicon* (github.com/adoxa/ansicon) for windows which replaces *cmd.exe*. + +For a windows user the simplest way to implement ansicon is to use the *geany* +editor and in the Edit->Preferences->Tools menu replace *cmd.exe* with +*ansicon.exe* (be sure to supply the path to *ansicon*). + +If *enhanced_print* is called in a program (linux) when multivectors are printed +the basis blades or bases are printed in bold text, functions are printed in red, +and derivative operators in green. + +For formatting the multivector output there is the member function + +.. function:: sympy.galgebra.ga.Fmt(self, fmt=1, title=None) + +*Fmt* is used to control how the multivector is printed with the argument +*fmt*. If *fmt=1* the entire multivector is printed on one line. If +*fmt=2* each grade of the multivector is printed on one line. If *fmt=3* +each component (base) of the multivector is printed on one line. If a +*title* is given then *title = multivector* is printed. If the usual print +command is used the entire multivector is printed on one line. + +.. function:: sympy.galgebra.ga.ga_print_on() + +Redirects printer output from standard *sympy* print handler. Needed if +one wishes to use compact forms of *function* and *derivative* output +strings for interactive use. + +.. function:: sympy.galgebra.ga.ga_print_off() + +Restores standard *sympy* print handler. + +.. function:: sympy.galgebra.printing.GA_Printer() + +Context handler for use inside Sympy code. The code indented under the +*with GA_Printer():* statement will run with the compact forms of *function* and +*derivative* output strings, and have the printing restored to standard *sympy* +printing after it has finished, even if it is aborted with an exception. + + +Latex Printing +-------------- + +For latex printing one uses one functions from the *galgebra* module and one +function from the *galgebra/printing* module. The +functions are + +.. function:: sympy.galgebra.printing.Format(Fmode=True, Dmode=True, ipy=False) + + This function from the *galgebra* module turns on latex printing with the + following options + + .. list-table:: + :widths: 15, 15, 55 + :header-rows: 1 + + * - argument + - value + - result + * - *Fmode* + - *True* + - Print functions without argument list, :math:`f` + * - + - *False* + - Print functions with standard sympy latex formatting, :math:`f(x,y,z)` + * - *Dmode* + - *True* + - Print partial derivatives with condensed notatation, :math:`\partial_{x}f` + * - + - *False* + - Print partial derivatives with standard sympy latex formatting :math:`\frac{\partial f}{\partial x}` + * - *ipy* + - *False* + - Redirect print output to file for post-processing by latex + * - + - *True* + - Do not redirect print output (This is used for Ipython with MathJax) + + +.. function:: sympy.galgebra.printing.xdvi(filename=None, pdf='', debug=False, paper=(14, 11)) + + This function from the *galgebra/printing* module post-processes the output captured from + print statements. Write the resulting latex strings to the file *filename*, + processes the file with pdflatex, and displays the resulting pdf file. *pdf* is the name of the + pdf viewer on your computer. If you are running *ubuntu* the *evince* viewer is automatically + used. On other operating systems if *pdf = ''* the name of the pdf file is executed. If the + pdf file type is associated with a viewer this will launch the viewer with the associated file. + All latex files except + the pdf file are deleted. If *debug = True* the file *filename* is printed to + standard output for debugging purposes and *filename* (the tex file) is saved. If *filename* is not entered the default + filename is the root name of the python program being executed with *.tex* appended. The format for the *paper* is + + .. list-table:: + :widths: 25, 65 + :header-rows: 0 + + * - *paper=(w,h)* + - *w* is paper width in inches and,*h* is paper height in inches + * - *paper='letter'* + - paper is standard leter size :math:`8.5\mbox{ in}\times 11\mbox{ in}` + + The default of *paper=(14,11)* was chosen so that long multivector expressions would not be truncated on + the display. + + The **xdvi** function requires that latex and a pdf viewer be installed on + the computer. + +As an example of using the latex printing options when the following code is +executed + + .. code-block:: python + + from sympy.galgebra.printing import xdvi + from sympy.galgebra.ga import * + Format() + (ex,ey,ez) = MV.setup('e*x|y|z') + A = MV('A','mv') + print r'\bm{A} =',A + A.Fmt(2,r'\bm{A}') + A.Fmt(3,r'\bm{A}') + + xdvi() + + + +The following is displayed + + .. math:: + :nowrap: + + \begin{align*} + \boldsymbol{A} = & A+A^{x}\boldsymbol{e_{x}}+A^{y}\boldsymbol{e_{y}}+A^{z}\boldsymbol{e_{z}}+A^{xy}\boldsymbol{e_{x}\wedge e_{y}}+A^{xz}\boldsymbol{e_{x}\wedge e_{z}}+A^{yz}\boldsymbol{e_{y}\wedge e_{z}}+A^{xyz}\boldsymbol{e_{x}\wedge e_{y}\wedge e_{z}} \\ + \boldsymbol{A} = & A \\ & +A^{x}\boldsymbol{e_{x}}+A^{y}\boldsymbol{e_{y}}+A^{z}\boldsymbol{e_{z}} \\ & +A^{xy}\boldsymbol{e_{x}\wedge e_{y}}+A^{xz}\boldsymbol{e_{x}\wedge e_{z}}+A^{yz}\boldsymbol{e_{y}\wedge e_{z}} \\ & +A^{xyz}\boldsymbol{e_{x}\wedge e_{y}\wedge e_{z}} \\ + \boldsymbol{A} = & A \\ & +A^{x}\boldsymbol{e_{x}} \\ & +A^{y}\boldsymbol{e_{y}} \\ & +A^{z}\boldsymbol{e_{z}} \\ & +A^{xy}\boldsymbol{e_{x}\wedge e_{y}} \\ & +A^{xz}\boldsymbol{e_{x}\wedge e_{z}} \\ & +A^{yz}\boldsymbol{e_{y}\wedge e_{z}} \\ & +A^{xyz}\boldsymbol{e_{x}\wedge e_{y}\wedge e_{z}} + \end{align*} + + + +For the cases of derivatives the code is + + .. code-block:: python + + from sympy.galgebra.printing import xdvi + from sympy.galgebra.ga import * + + Format() + X = (x,y,z) = symbols('x y z') + (ex,ey,ez,grad) = MV.setup('e_x e_y e_z',metric='[1,1,1]',coords=X) + + f = MV('f','scalar',fct=True) + A = MV('A','vector',fct=True) + B = MV('B','grade2',fct=True) + + print r'\bm{A} =',A + print r'\bm{B} =',B + + print 'grad*f =',grad*f + print r'grad|\bm{A} =',grad|A + print r'grad*\bm{A} =',grad*A + + print r'-I*(grad^\bm{A}) =',-MV.I*(grad^A) + print r'grad*\bm{B} =',grad*B + print r'grad^\bm{B} =',grad^B + print r'grad|\bm{B} =',grad|B + + xdvi() + +and the latex displayed output is (:math:`f` is a scalar function) + + .. math:: + :nowrap: + + \begin{align*} + \boldsymbol{A} =& A^{x}\boldsymbol{e_{x}}+A^{y}\boldsymbol{e_{y}}+A^{z}\boldsymbol{e_{z}} \\ + \boldsymbol{B} =& B^{xy}\boldsymbol{e_{x}\wedge e_{y}}+B^{xz}\boldsymbol{e_{x}\wedge e_{z}}+B^{yz}\boldsymbol{e_{y}\wedge e_{z}} \\ + \boldsymbol{\nabla} f =& \partial_{x} f\boldsymbol{e_{x}}+\partial_{y} f\boldsymbol{e_{y}}+\partial_{z} f\boldsymbol{e_{z}} \\ + \boldsymbol{\nabla} \cdot \boldsymbol{A} = &\partial_{x} A^{x} + \partial_{y} A^{y} + \partial_{z} A^{z} \\ + \boldsymbol{\nabla} \boldsymbol{A} = &\partial_{x} A^{x} + \partial_{y} A^{y} + \partial_{z} A^{z} + +\left ( - \partial_{y} A^{x} + \partial_{x} A^{y}\right ) \boldsymbol{e_{x}\wedge e_{y}} + +\left ( - \partial_{z} A^{x} + \partial_{x} A^{z}\right ) \boldsymbol{e_{x}\wedge e_{z}} \\ + &+\left ( - \partial_{z} A^{y} + \partial_{y} A^{z}\right ) \boldsymbol{e_{y}\wedge e_{z}} \\ + -I (\boldsymbol{\nabla} \wedge \boldsymbol{A}) = &\left ( - \partial_{z} A^{y} + \partial_{y} A^{z}\right ) \boldsymbol{e_{x}} + +\left ( \partial_{z} A^{x} - \partial_{x} A^{z}\right ) \boldsymbol{e_{y}} + +\left ( - \partial_{y} A^{x} + \partial_{x} A^{y}\right ) \boldsymbol{e_{z}} \\ + \boldsymbol{\nabla} \boldsymbol{B} = &\left ( - \partial_{y} B^{xy} - \partial_{z} B^{xz}\right ) \boldsymbol{e_{x}} + +\left ( \partial_{x} B^{xy} - \partial_{z} B^{yz}\right ) \boldsymbol{e_{y}} + +\left ( \partial_{x} B^{xz} + \partial_{y} B^{yz}\right ) \boldsymbol{e_{z}} \\ + &+\left ( \partial_{z} B^{xy} - \partial_{y} B^{xz} + \partial_{x} B^{yz}\right ) \boldsymbol{e_{x}\wedge e_{y}\wedge e_{z}} \\ + \boldsymbol{\nabla} \wedge \boldsymbol{B} = &\left ( \partial_{z} B^{xy} - \partial_{y} B^{xz} + \partial_{x} B^{yz}\right ) \boldsymbol{e_{x}\wedge e_{y}\wedge e_{z}} \\ + \boldsymbol{\nabla} \cdot \boldsymbol{B} = &\left ( - \partial_{y} B^{xy} - \partial_{z} B^{xz}\right ) \boldsymbol{e_{x}}+\left ( \partial_{x} B^{xy} - \partial_{z} B^{yz}\right ) \boldsymbol{e_{y}}+\left ( \partial_{x} B^{xz} + \partial_{y} B^{yz}\right ) \boldsymbol{e_{z}} + \end{align*} + + + +This example also demonstrates several other features of the latex printer. In the +case that strings are input into the latex printer such as ``r'grad*\boldsymbol{A}'``, +``r'grad^\boldsymbol{A}'``, or ``r'grad*\boldsymbol{A}'``. The text symbols *grad*, *^*, *|*, and +*\ ** are mapped by the *xdvi()* post-processor as follows if the string contains +an *=*. + + .. csv-table:: + :header: original , replacement , displayed latex + :widths: 15, 15, 15 + + ``grad*A`` , ``\boldsymbol{\nabla}A`` , :math:`\boldsymbol{\nabla}A` + ``A^B`` , ``A\wedge B`` , :math:`A\wedge B` + ``A|B`` , ``A\cdot B`` , :math:`A\cdot B` + ``A*B`` , ``AB`` , :math:`AB` + ``AB`` , ``A\rfloor B`` , :math:`A\rfloor B` + +If the string to be printed contains a *\%* none of the above substitutions +are made before the latex processor is applied. In general for the latex +printer strings are assumed to be in a math environment (*equation\ ** or +*align\ **) unless the string contains a *\#*. + +.. note:: + + Except where noted the conventions for latex printing follow those of the + latex printing module of sympy. This includes translating sympy variables + with Greek name (such as ``alpha``) to the equivalent Greek symbol + (:math:`\alpha`) for the purpose of latex printing. Also a single + underscore in the variable name (such as ``X_j``) indicates a subscript + (:math:`X_{j}`), and a double underscore (such as ``X__k``) a + superscript (:math:`X^{k}`). The only other change with regard to the + sympy latex printer is that matrices are printed full size (equation + displaystyle). + + +Printer Redirection +------------------- + +In order to print transparently, that is to simply use the *print* statement +with both text and LaTeX printing the printer output is redirected. In +the case of text printing the reason for redirecting the printer output +is because the *sympy* printing functions *_print_Derivative* and +*_print_Function* are redefined to make the output more compact. If one +does not wish to use the compact notation redirection is not required for +the text printer. If one wishes to use the redefined *_print_Derivative* +and *_print_Function* the printer should be redirected with the function +*ga_print_on()* and restored with the function *ga_print_off()*. Both +functions can be imported from *sympy.galgebra.ga* +(see *examples/galgebra/terminal_check.py* for usage). + +SymPy provides a context handler that will allow you to rewrite any +*ga_print_on(); "do something"; ga_print_off();* sequence like this: + + .. code-block:: python + + with GA_printer: + "do something" + +This has the advantage that even in the case of an exception inside "do something", +the *ga_print_off();* call will be made. + +For LaTeX printing the *Format()* (import from *sympy.galgebra.ga*) redirects the printer output to a +string. After all printing requests one must call the function *xdvi()* +(import from *sympy.galgebra.printing*) tp process the string to a LaTeX format, compile with +pdflatex, and displayed the resulting pdf file. The function *xdvi()* +also restores the printer output to normal for standard *sympy* printing. +If *Format(ipy=True)* is used there is no printer redirection and the +LaTeX output is simply sent to *sys.stdout* for use in *Ipython* +(*Ipython* LaTeX interface for *galgebra* not yet implemented). + + +Other Printing Functions +------------------------ + +These functions are used together if one wishes to print both code and +output in a single file. They work for text printing and for latex printing. + +For these functions to work properly the last function defined must not +contain a *Print_Function()* call (the last function defined is usually a +*dummy()* function that does nothing). + +Additionally, to work properly none of the functions containing *Print_Function()* +can contain function definintions (local functions). + +.. function:: sympy.galgebra.printing.Get_Program(off=False) + + Tells program to print both code and output from functions that have been + properly tagged with *Print_Function()*. *Get_Program()* must be in + main program before the functions that you wish code printing from are + executed. the *off* argument in *Get_Program()* allows one to turn off + the printing of the code by changing one line in the entire program + (*off=True*). + +.. function:: sympy.galgebra.printing.Print_Function() + + *Print_Function()* is included in those functions where one wishes to + print the code block along with (before) the usual printed output. The + *Print_Function()* statement should be included immediately after the + function def statement. For proper usage of both *Print_Function()* + and *Get_Program()* see the following example. + +As an example consider the following code + + .. code-block:: python + + from sympy.galgebra.printing import xdvi,Get_Program,Print_Function + from sympy.galgebra.ga import * + + Format() + + def basic_multivector_operations_3D(): + Print_Function() + (ex,ey,ez) = MV.setup('e*x|y|z') + + A = MV('A','mv') + + A.Fmt(1,'A') + A.Fmt(2,'A') + A.Fmt(3,'A') + + A.even().Fmt(1,'%A_{+}') + A.odd().Fmt(1,'%A_{-}') + + X = MV('X','vector') + Y = MV('Y','vector') + + print 'g_{ij} =',MV.metric + + X.Fmt(1,'X') + Y.Fmt(1,'Y') + + (X*Y).Fmt(2,'X*Y') + (X^Y).Fmt(2,'X^Y') + (X|Y).Fmt(2,'X|Y') + return + + def basic_multivector_operations_2D(): + Print_Function() + (ex,ey) = MV.setup('e*x|y') + + print 'g_{ij} =',MV.metric + + X = MV('X','vector') + A = MV('A','spinor') + + X.Fmt(1,'X') + A.Fmt(1,'A') + + (X|A).Fmt(2,'X|A') + (XX).Fmt(2,'A>X') + return + + def dummy(): + return + + Get_Program() + + basic_multivector_operations_3D() + basic_multivector_operations_2D() + + xdvi() + +The latex output of the code is + +.. image:: simple_test_latex_1.png + +| + +.. image:: simple_test_latex_2.png + +Examples +======== + + +Algebra +------- + +BAC-CAB Formulas +++++++++++++++++ + +This example demonstrates the most general metric tensor + +.. math:: + :nowrap: + + \begin{equation*} + g_{ij} = \left [ \begin{array}{cccc} \left ( a\cdot a\right ) & \left ( a\cdot b\right ) & \left ( a\cdot c\right ) & \left ( a\cdot d\right ) \\ + \left ( a\cdot b\right ) & \left ( b\cdot b\right ) & \left ( b\cdot c\right ) & \left ( b\cdot d\right ) \\ + \left ( a\cdot c\right ) & \left ( b\cdot c\right ) & \left ( c\cdot c\right ) & \left ( c\cdot d\right ) \\ + \left ( a\cdot d\right ) & \left ( b\cdot d\right ) & \left ( c\cdot d\right ) & \left ( d\cdot d\right ) + \end{array}\right ] + \end{equation*} + + + +and how the *galgebra* module can be used to verify and expand geometric algebra identities consisting of relations between +the abstract vectors :math:`a`, :math:`b`, :math:`c`, and :math:`d`. + +.. code-block:: python + + from sympy.galgebra.printing import xdvi + from sympy.galgebra.ga import * + Format() + + (a,b,c,d) = MV.setup('a b c d') + print '\\bm{a|(b*c)} =',a|(b*c) + print '\\bm{a|(b^c)} =',a|(b^c) + print '\\bm{a|(b^c^d)} =',a|(b^c^d) + print '\\bm{a|(b^c)+c|(a^b)+b|(c^a)} =',(a|(b^c))+(c|(a^b))+(b|(c^a)) + print '\\bm{a*(b^c)-b*(a^c)+c*(a^b)} =',a*(b^c)-b*(a^c)+c*(a^b) + print '\\bm{a*(b^c^d)-b*(a^c^d)+c*(a^b^d)-d*(a^b^c)} =',a*(b^c^d)-b*(a^c^d)+c*(a^b^d)-d*(a^b^c) + print '\\bm{(a^b)|(c^d)} =',(a^b)|(c^d) + print '\\bm{((a^b)|c)|d} =',((a^b)|c)|d + print '\\bm{(a^b)\\times (c^d)} =',Com(a^b,c^d) + + xdvi() + +The preceeding code block also demonstrates the mapping of *\ **, *^*, and *|* to appropriate latex +symbols. + +.. note:: + + The :math:`\times` symbol is the commutator product of two multivectors, :math:`A\times B = (AB-BA)/2`. + +.. math:: + :nowrap: + + \begin{align*} + \boldsymbol{a\cdot (b c)} =& - \left ( a\cdot c\right ) \boldsymbol{b}+\left ( a\cdot b\right ) \boldsymbol{c} \\ + \boldsymbol{a\cdot (b\wedge c)} =& - \left ( a\cdot c\right ) \boldsymbol{b}+\left ( a\cdot b\right ) \boldsymbol{c} \\ + \boldsymbol{a\cdot (b\wedge c\wedge d)} =& \left ( a\cdot d\right ) \boldsymbol{b\wedge c}- \left ( a\cdot c\right ) \boldsymbol{b\wedge d}+\left ( a\cdot b\right ) \boldsymbol{c\wedge d} \\ + \boldsymbol{a\cdot (b\wedge c)+c\cdot (a\wedge b)+b\cdot (c\wedge a)} =& 0 \\ + \boldsymbol{a (b\wedge c)-b (a\wedge c)+c (a\wedge b)} =& 3\boldsymbol{a\wedge b\wedge c} \\ + \boldsymbol{a (b\wedge c\wedge d)-b (a\wedge c\wedge d)+c (a\wedge b\wedge d)-d (a\wedge b\wedge c)} =& 4\boldsymbol{a\wedge b\wedge c\wedge d} \\ + \boldsymbol{(a\wedge b)\cdot (c\wedge d)} =& - \left ( a\cdot c\right ) \left ( b\cdot d\right ) + \left ( a\cdot d\right ) \left ( b\cdot c\right ) \\ + \boldsymbol{((a\wedge b)\cdot c)\cdot d} =& - \left ( a\cdot c\right ) \left ( b\cdot d\right ) + \left ( a\cdot d\right ) \left ( b\cdot c\right ) \\ + \boldsymbol{(a\wedge b)\times (c\wedge d)} =& - \left ( b\cdot d\right ) \boldsymbol{a\wedge c}+\left ( b\cdot c\right ) \boldsymbol{a\wedge d}+\left ( a\cdot d\right ) \boldsymbol{b\wedge c}- \left ( a\cdot c\right ) \boldsymbol{b\wedge d} + \end{align*} + + + +Reciprocal Frame +++++++++++++++++ + +The reciprocal frame of vectors with respect to the basis vectors is required +for the evaluation of the geometric dervative. The following example demonstrates +that for the case of an arbitrary 3-dimensional Euclidian basis the reciprocal +basis vectors are correctly calculated. + +.. code-block:: python + + from sympy.galgebra.printing import xdvi + from sympy.galgebra.ga import * + Format() + + metric = '1 # #,'+ \ + '# 1 #,'+ \ + '# # 1,' + + (e1,e2,e3) = MV.setup('e1 e2 e3',metric) + + E = e1^e2^e3 + Esq = (E*E).scalar() + print 'E =',E + print '%E^{2} =',Esq + Esq_inv = 1/Esq + + E1 = (e2^e3)*E + E2 = (-1)*(e1^e3)*E + E3 = (e1^e2)*E + + print 'E1 = (e2^e3)*E =',E1 + print 'E2 =-(e1^e3)*E =',E2 + print 'E3 = (e1^e2)*E =',E3 + + print 'E1|e2 =',(E1|e2).expand() + print 'E1|e3 =',(E1|e3).expand() + print 'E2|e1 =',(E2|e1).expand() + print 'E2|e3 =',(E2|e3).expand() + print 'E3|e1 =',(E3|e1).expand() + print 'E3|e2 =',(E3|e2).expand() + w = ((E1|e1).expand()).scalar() + Esq = expand(Esq) + print '%(E1\\cdot e1)/E^{2} =',simplify(w/Esq) + w = ((E2|e2).expand()).scalar() + print '%(E2\\cdot e2)/E^{2} =',simplify(w/Esq) + w = ((E3|e3).expand()).scalar() + print '%(E3\\cdot e3)/E^{2} =',simplify(w/Esq) + + xdvi() + +The preceeding code also demonstrated the use of the *\%* directive in +printing a string so that *^* is treated literally and not translated +to *\\wedge*. Note that ``'%E^{2} ='`` is printed as :math:`E^{2} =` +and not as :math:`E\wedge {2} =`. + +.. math:: + :nowrap: + + \begin{align*} + E =& \boldsymbol{e_{1}\wedge e_{2}\wedge e_{3}} \\ + E^{2} =& \left ( e_{1}\cdot e_{2}\right ) ^{2} - 2 \left ( e_{1}\cdot e_{2}\right ) \left ( e_{1}\cdot e_{3}\right ) \left ( e_{2}\cdot e_{3}\right ) + \left ( e_{1}\cdot e_{3}\right ) ^{2} + \left ( e_{2}\cdot e_{3}\right ) ^{2} -1 \\ + E1 =& (e2\wedge e3) E = \left ( \left ( e_{2}\cdot e_{3}\right ) ^{2} -1\right ) \boldsymbol{e_{1}}+\left ( \left ( e_{1}\cdot e_{2}\right ) - \left ( e_{1}\cdot e_{3}\right ) \left ( e_{2}\cdot e_{3}\right ) \right ) \boldsymbol{e_{2}}+\left ( - \left ( e_{1}\cdot e_{2}\right ) \left ( e_{2}\cdot e_{3}\right ) + \left ( e_{1}\cdot e_{3}\right ) \right ) \boldsymbol{e_{3}} \\ + E2 =& -(e1\wedge e3) E = \left ( \left ( e_{1}\cdot e_{2}\right ) - \left ( e_{1}\cdot e_{3}\right ) \left ( e_{2}\cdot e_{3}\right ) \right ) \boldsymbol{e_{1}}+\left ( \left ( e_{1}\cdot e_{3}\right ) ^{2} -1\right ) \boldsymbol{e_{2}}+\left ( - \left ( e_{1}\cdot e_{2}\right ) \left ( e_{1}\cdot e_{3}\right ) + \left ( e_{2}\cdot e_{3}\right ) \right ) \boldsymbol{e_{3}} \\ + E3 =& (e1\wedge e2) E = \left ( - \left ( e_{1}\cdot e_{2}\right ) \left ( e_{2}\cdot e_{3}\right ) + \left ( e_{1}\cdot e_{3}\right ) \right ) \boldsymbol{e_{1}}+\left ( - \left ( e_{1}\cdot e_{2}\right ) \left ( e_{1}\cdot e_{3}\right ) + \left ( e_{2}\cdot e_{3}\right ) \right ) \boldsymbol{e_{2}}+\left ( \left ( e_{1}\cdot e_{2}\right ) ^{2} -1\right ) \boldsymbol{e_{3}} \\ + E1\cdot e2 =& 0 \\ + E1\cdot e3 =& 0 \\ + E2\cdot e1 =& 0 \\ + E2\cdot e3 =& 0 \\ + E3\cdot e1 =& 0 \\ + E3\cdot e2 =& 0 \\ + (E1\cdot e1)/E^{2} =& 1 \\ + (E2\cdot e2)/E^{2} =& 1 \\ + (E3\cdot e3)/E^{2} =& 1 + \end{align*} + + + +The formulas derived for :math:`E1`, :math:`E2`, :math:`E3`, and :math:`E^{2}` could +also be applied to the numerical calculations of crystal properties. + +Lorentz-Transformation +++++++++++++++++++++++ + +A simple physics demonstation of geometric algebra is the derivation of +the Lorentz-Transformation. In this demonstration a 2-dimensional +Minkowski space is defined and the Lorentz-Transformation is generated +from a rotation of a vector in the Minkowski space using the rotor +:math:`R`. + +.. code-block:: python + + from sympy import symbols,sinh,cosh + from sympy.galgebra.printing import xdvi + from sympy.galgebra.ga import * + + Format() + (alpha,beta,gamma) = symbols('alpha beta gamma') + (x,t,xp,tp) = symbols("x t x' t'") + (g0,g1) = MV.setup('gamma*t|x',metric='[1,-1]') + + R = cosh(alpha/2)+sinh(alpha/2)*(g0^g1) + X = t*g0+x*g1 + Xp = tp*g0+xp*g1 + + print 'R =',R + print r"#%t\bm{\gamma_{t}}+x\bm{\gamma_{x}} = t'\bm{\gamma'_{t}}+x'\bm{\gamma'_{x}} "+ + r"= R\left ( t'\bm{\gamma_{t}}+x'\bm{\gamma_{x}}\right ) R^{\dagger}" + + Xpp = R*Xp*R.rev() + Xpp = Xpp.collect([xp,tp]) + Xpp = Xpp.subs({2*sinh(alpha/2)*cosh(alpha/2):sinh(alpha),\ + sinh(alpha/2)**2+cosh(alpha/2)**2:cosh(alpha)}) + print r"%t\bm{\gamma_{t}}+x\bm{\gamma_{x}} =",Xpp + Xpp = Xpp.subs({sinh(alpha):gamma*beta,cosh(alpha):gamma}) + + print r'%\f{\sinh}{\alpha} = \gamma\beta' + print r'%\f{\cosh}{\alpha} = \gamma' + + print r"%t\bm{\gamma_{t}}+x\bm{\gamma_{x}} =",Xpp.collect(gamma) + + xdvi() + +The preceeding code also demonstrates how to use the sympy *subs* functions +to perform the hyperbolic half angle transformation. The code also shows +the use of both the *#* and *\%* directives in the text string +``r"#%t\bm{\gamma_{t}}+x\bm{\gamma_{x}} = t'\bm{\gamma'_{t}}+x'\bm{\gamma'_{x}} = R\left ( t'\bm{\gamma_{t}}+x'\bm{\gamma_{x}}\right ) R^{\dagger}"``. +Both the *#* and *\%* are needed in this text string for two reasons. First, the text string contains an *=* sign. The latex preprocessor +uses this a key to combine the text string with a sympy expression to be printed after the text string. The *#* is required to inform +the preprocessor that there is no sympy expression to follow. Second, the *\%* is requires to inform the preprocessor that the text +string is to be displayed in latex math mode and not in text mode (if *#* is present the default latex mode is text mode unless +overridden by the *\%* directive). + +.. math:: + :nowrap: + + \begin{align*} R =& \cosh{\left (\frac{1}{2} \alpha \right )}+\sinh{\left (\frac{1}{2} \alpha \right )}\boldsymbol{\gamma_{t}\wedge \gamma_{x}} \\ + t\boldsymbol{\gamma_{t}}+x\boldsymbol{\gamma_{x}} =& t'\boldsymbol{\gamma'_{t}}+x'\boldsymbol{\gamma'_{x}} = R\left ( t'\boldsymbol{\gamma_{t}}+x'\boldsymbol{\gamma_{x}}\right ) R^{\dagger} \\ + t\boldsymbol{\gamma_{t}}+x\boldsymbol{\gamma_{x}} =& \left ( t' \cosh{\left (\alpha \right )} - x' \sinh{\left (\alpha \right )}\right ) \boldsymbol{\gamma_{t}}+\left ( - t' \sinh{\left (\alpha \right )} + x' \cosh{\left (\alpha \right )}\right ) \boldsymbol{\gamma_{x}} \\ + {\sinh}\left ( {\alpha} \right ) =& \gamma\beta \\ + {\cosh}\left ( {\alpha} \right ) =& \gamma \\ + t\boldsymbol{\gamma_{t}}+x\boldsymbol{\gamma_{x}} =& \left ( \gamma \left(- \beta x' + t'\right)\right ) \boldsymbol{\gamma_{t}}+\left ( \gamma \left(- \beta t' + x'\right)\right ) \boldsymbol{\gamma_{x}} + \end{align*} + + + + +Calculus +-------- + + +Derivatives in Spherical Coordinates +++++++++++++++++++++++++++++++++++++ + +The following code shows how to use *galgebra* to use spherical coordinates. +The gradient of a scalar function, :math:`f`, the divergence and curl +of a vector function, :math:`A`, and the exterior derivative (curl) of +a bivector function, :math:`B` are calculated. Note that to get the +standard curl of a 3-dimension function the result is multiplied by +:math:`-I` the negative of the pseudoscalar. + +.. note:: + + In geometric calculus the operator :math:`\nabla^{2}` is well defined + on its own as the geometic derivative of the geometric derivative. + However, if needed we have for the vector function :math:`A` the relations + (since :math:`\nabla\cdot A` is a scalar it's curl is equal to it's + geometric derivative and it's divergence is zero) - + + .. math:: + :nowrap: + + \begin{align*} + \nabla A =& \nabla\wedge A + \nabla\cdot A \\ + \nabla^{2} A =& \nabla\left ( {{\nabla\wedge A}} \right ) + \nabla\left ( {{\nabla\cdot A}} \right ) \\ + \nabla^{2} A =& \nabla\wedge\left ( {{\nabla\wedge A}} \right ) + \nabla\cdot\left ( {{\nabla\wedge A}} \right ) + +\nabla\wedge\left ( {{\nabla\cdot A}} \right ) + \nabla\cdot\left ( {{\nabla\cdot A}} \right ) \\ + \nabla^{2} A =& \nabla\wedge\left ( {{\nabla\wedge A}} \right ) + \left ( {{\nabla\cdot\nabla}} \right ) A + - \nabla\left ( {{\nabla\cdot A}} \right ) + \nabla\left ( {{\nabla\cdot A}} \right ) \\ + \nabla^{2} A =& \nabla\wedge\nabla\wedge A + \left ( {{\nabla\cdot\nabla}} \right )A + \end{align*} + + + + In the derivation we have used that :math:`\nabla\cdot\left ( {{\nabla\wedge A}} \right ) = \left ( {{\nabla\cdot\nabla}} \right )A - \nabla\left ( {{\nabla\cdot A}} \right )` + which is implicit in the second *BAC-CAB* formula. + No parenthesis is needed for the geometric curl of the curl (exterior derivative of exterior derivative) + since the :math:`\wedge` operation is associative unlike the vector curl operator and :math:`\nabla\cdot\nabla` is the usual Laplacian + operator. + +.. code-block:: python + + from sympy import sin,cos + from sympy.galgebra.printing import xdvi + from sympy.galgebra.ga import * + Format() + + X = (r,th,phi) = symbols('r theta phi') + curv = [[r*cos(phi)*sin(th),r*sin(phi)*sin(th),r*cos(th)],[1,r,r*sin(th)]] + (er,eth,ephi,grad) = MV.setup('e_r e_theta e_phi',metric='[1,1,1]',coords=X,curv=curv) + + f = MV('f','scalar',fct=True) + A = MV('A','vector',fct=True) + B = MV('B','grade2',fct=True) + + print 'A =',A + print 'B =',B + + print 'grad*f =',grad*f + print 'grad|A =',grad|A + print '-I*(grad^A) =',-MV.I*(grad^A) + print 'grad^B =',grad^B + + xdvi() + +Results of code + +.. math:: + :nowrap: + + \begin{align*} + A =& A^{r}\boldsymbol{e_{r}}+A^{\theta}\boldsymbol{e_{\theta}}+A^{\phi}\boldsymbol{e_{\phi}} \\ + B =& B^{r\theta}\boldsymbol{e_{r}\wedge e_{\theta}}+B^{r\phi}\boldsymbol{e_{r}\wedge e_{\phi}}+B^{\theta\phi}\boldsymbol{e_{\theta}\wedge e_{\phi}} \\ + \boldsymbol{\nabla} f =& \partial_{r} f\boldsymbol{e_{r}}+\frac{\partial_{\theta} f}{r}\boldsymbol{e_{\theta}}+\frac{\partial_{\phi} f}{r \sin{\left (\theta \right )}}\boldsymbol{e_{\phi}} \\ + \boldsymbol{\nabla} \cdot A =& \partial_{r} A^{r} + \frac{A^{\theta}}{r \tan{\left (\theta \right )}} + 2 \frac{A^{r}}{r} + \frac{\partial_{\theta} A^{\theta}}{r} + \frac{\partial_{\phi} A^{\phi}}{r \sin{\left (\theta \right )}} \\ + -I (\boldsymbol{\nabla} \wedge A) =& \left ( \frac{A^{\phi} \cos{\left (\theta \right )} + \sin{\left (\theta \right )} \partial_{\theta} A^{\phi} - \partial_{\phi} A^{\theta}}{r \sin{\left (\theta \right )}}\right ) \boldsymbol{e_{r}}+\left ( - \partial_{r} A^{\phi} - \frac{A^{\phi}}{r} + \frac{\partial_{\phi} A^{r}}{r \sin{\left (\theta \right )}}\right ) \boldsymbol{e_{\theta}}+\left ( \frac{r \partial_{r} A^{\theta} + A^{\theta} - \partial_{\theta} A^{r}}{r}\right ) \boldsymbol{e_{\phi}} \\ + \boldsymbol{\nabla} \wedge B =& \left ( \partial_{r} B^{\theta\phi} + 2 \frac{B^{\theta\phi}}{r} - \frac{B^{r\phi}}{r \tan{\left (\theta \right )}} - \frac{\partial_{\theta} B^{r\phi}}{r} + \frac{\partial_{\phi} B^{r\theta}}{r \sin{\left (\theta \right )}}\right ) \boldsymbol{e_{r}\wedge e_{\theta}\wedge e_{\phi}} + \end{align*} + + + +Maxwell's Equations ++++++++++++++++++++ + +The geometric algebra formulation of Maxwell's equations is deomonstrated +with the formalism developed in "Geometric Algebra for Physicists" [Doran]_. +In this formalism the signature of the metric is :math:`(1,-1,-1,-1)` and the +basis vectors are :math:`\gamma_{t}`, :math:`\gamma_{x}`, :math:`\gamma_{y}`, +and :math:`\gamma_{z}`. The if :math:`\boldsymbol{E}` and :math:`\boldsymbol{B}` are the +normal electric and magnetic field vectors the electric and magnetic +bivectors are given by :math:`E = \boldsymbol{E}\gamma_{t}` and :math:`B = \boldsymbol{B}\gamma_{t}`. +The electromagnetic bivector is then :math:`F = E+IB` where +:math:`I = \gamma_{t}\gamma_{x}\gamma_{y}\gamma_{z}` is the pesudo-scalar +for the Minkowski space. Note that the electromagnetic bivector is isomorphic +to the electromagnetic tensor. Then if :math:`J` is the 4-current all of +Maxwell's equations are given by :math:`\boldsymbol{\nabla}F = J`. For more details +see [Doran]_ chapter 7. + +.. code-block:: python + + from sympy import symbols,sin,cos + from sympy.galgebra.printing import xdvi + from sympy.galgebra.ga import * + + Format() + + vars = symbols('t x y z') + (g0,g1,g2,g3,grad) = MV.setup('gamma*t|x|y|z',metric='[1,-1,-1,-1]',coords=vars) + I = MV.I + + B = MV('B','vector',fct=True) + E = MV('E','vector',fct=True) + B.set_coef(1,0,0) + E.set_coef(1,0,0) + B *= g0 + E *= g0 + J = MV('J','vector',fct=True) + F = E+I*B + + print 'B = \\bm{B\\gamma_{t}} =',B + print 'E = \\bm{E\\gamma_{t}} =',E + print 'F = E+IB =',F + print 'J =',J + gradF = grad*F + gradF.Fmt(3,'grad*F') + + print 'grad*F = J' + (gradF.grade(1)-J).Fmt(3,'%\\grade{\\nabla F}_{1} -J = 0') + (gradF.grade(3)).Fmt(3,'%\\grade{\\nabla F}_{3} = 0') + + xdvi() + +.. math:: + :nowrap: + + \begin{align*} + B =& \boldsymbol{B\gamma_{t}} = - B^{x}\boldsymbol{\gamma_{t}\wedge \gamma_{x}}- B^{y}\boldsymbol{\gamma_{t}\wedge \gamma_{y}}- B^{z}\boldsymbol{\gamma_{t}\wedge \gamma_{z}} \\ + E =& \boldsymbol{E\gamma_{t}} = - E^{x}\boldsymbol{\gamma_{t}\wedge \gamma_{x}}- E^{y}\boldsymbol{\gamma_{t}\wedge \gamma_{y}}- E^{z}\boldsymbol{\gamma_{t}\wedge \gamma_{z}} \\ + F =& E+IB = - E^{x}\boldsymbol{\gamma_{t}\wedge \gamma_{x}}- E^{y}\boldsymbol{\gamma_{t}\wedge \gamma_{y}}- E^{z}\boldsymbol{\gamma_{t}\wedge \gamma_{z}}- B^{z}\boldsymbol{\gamma_{x}\wedge \gamma_{y}}+B^{y}\boldsymbol{\gamma_{x}\wedge \gamma_{z}}- B^{x}\boldsymbol{\gamma_{y}\wedge \gamma_{z}} \\ + J =& J^{t}\boldsymbol{\gamma_{t}}+J^{x}\boldsymbol{\gamma_{x}}+J^{y}\boldsymbol{\gamma_{y}}+J^{z}\boldsymbol{\gamma_{z}} \\ + \boldsymbol{\nabla} F =& \left ( \partial_{x} E^{x} + \partial_{y} E^{y} + \partial_{z} E^{z}\right ) \boldsymbol{\gamma_{t}} \\ + & +\left ( - \partial_{z} B^{y} + \partial_{y} B^{z} - \partial_{t} E^{x}\right ) \boldsymbol{\gamma_{x}} \\ + & +\left ( \partial_{z} B^{x} - \partial_{x} B^{z} - \partial_{t} E^{y}\right ) \boldsymbol{\gamma_{y}} \\ + & +\left ( - \partial_{y} B^{x} + \partial_{x} B^{y} - \partial_{t} E^{z}\right ) \boldsymbol{\gamma_{z}} \\ + & +\left ( - \partial_{t} B^{z} + \partial_{y} E^{x} - \partial_{x} E^{y}\right ) \boldsymbol{\gamma_{t}\wedge \gamma_{x}\wedge \gamma_{y}} \\ + & +\left ( \partial_{t} B^{y} + \partial_{z} E^{x} - \partial_{x} E^{z}\right ) \boldsymbol{\gamma_{t}\wedge \gamma_{x}\wedge \gamma_{z}} \\ + & +\left ( - \partial_{t} B^{x} + \partial_{z} E^{y} - \partial_{y} E^{z}\right ) \boldsymbol{\gamma_{t}\wedge \gamma_{y}\wedge \gamma_{z}} \\ + & +\left ( \partial_{x} B^{x} + \partial_{y} B^{y} + \partial_{z} B^{z}\right ) \boldsymbol{\gamma_{x}\wedge \gamma_{y}\wedge \gamma_{z}} \\ + \boldsymbol{\nabla} F =& J \\ + \left < {{\nabla F}} \right >_{1} -J = 0 = & \left ( - J^{t} + \partial_{x} E^{x} + \partial_{y} E^{y} + \partial_{z} E^{z}\right ) \boldsymbol{\gamma_{t}} \\ + & +\left ( - J^{x} - \partial_{z} B^{y} + \partial_{y} B^{z} - \partial_{t} E^{x}\right ) \boldsymbol{\gamma_{x}} \\ + & +\left ( - J^{y} + \partial_{z} B^{x} - \partial_{x} B^{z} - \partial_{t} E^{y}\right ) \boldsymbol{\gamma_{y}} \\ + & +\left ( - J^{z} - \partial_{y} B^{x} + \partial_{x} B^{y} - \partial_{t} E^{z}\right ) \boldsymbol{\gamma_{z}} \\ + \left < {{\nabla F}} \right >_{3} = 0 = & \left ( - \partial_{t} B^{z} + \partial_{y} E^{x} - \partial_{x} E^{y}\right ) \boldsymbol{\gamma_{t}\wedge \gamma_{x}\wedge \gamma_{y}} \\ + & +\left ( \partial_{t} B^{y} + \partial_{z} E^{x} - \partial_{x} E^{z}\right ) \boldsymbol{\gamma_{t}\wedge \gamma_{x}\wedge \gamma_{z}} \\ + & +\left ( - \partial_{t} B^{x} + \partial_{z} E^{y} - \partial_{y} E^{z}\right ) \boldsymbol{\gamma_{t}\wedge \gamma_{y}\wedge \gamma_{z}} \\ + & +\left ( \partial_{x} B^{x} + \partial_{y} B^{y} + \partial_{z} B^{z}\right ) \boldsymbol{\gamma_{x}\wedge \gamma_{y}\wedge \gamma_{z}} + \end{align*} + + + +Dirac Equation +++++++++++++++ + +In [Doran]_ equation 8.89 (page 283) is the geometric algebra formulation of the Dirac equation. In this equation +:math:`\psi` is an 8-component real spinor which is to say that it is a multivector with sacalar, bivector, and +pseudo-vector components in the space-time geometric algebra (it consists only of even grade components). + +.. code-block:: python + + from sympy import symbols,sin,cos + from sympy.galgebra.printing import xdvi + from sympy.galgebra.ga import * + + Format() + + vars = symbols('t x y z') + (g0,g1,g2,g3,grad) = MV.setup('gamma*t|x|y|z',metric='[1,-1,-1,-1]',coords=vars) + I = MV.I + (m,e) = symbols('m e') + + psi = MV('psi','spinor',fct=True) + A = MV('A','vector',fct=True) + sig_z = g3*g0 + print '\\bm{A} =',A + print '\\bm{\\psi} =',psi + + dirac_eq = (grad*psi)*I*sig_z-e*A*psi-m*psi*g0 + dirac_eq.simplify() + + dirac_eq.Fmt(3,r'\nabla \bm{\psi} I \sigma_{z}-e\bm{A}\bm{\psi}-m\bm{\psi}\gamma_{t} = 0') + + xdvi() + +The equations displayed are the partial differential equations for each component of the Dirac equation +in rectangular coordinates we the driver for the equations is the 4-potential :math:`A`. One utility +of these equations is to setup a numerical solver for the Dirac equation. + +.. math:: + :nowrap: + + + \begin{align*} + \boldsymbol{A} =& A^{t}\boldsymbol{\gamma_{t}}+A^{x}\boldsymbol{\gamma_{x}}+A^{y}\boldsymbol{\gamma_{y}}+A^{z}\boldsymbol{\gamma_{z}} \\ + \boldsymbol{\psi} =& \psi+\psi^{tx}\boldsymbol{\gamma_{t}\wedge \gamma_{x}} + +\psi^{ty}\boldsymbol{\gamma_{t}\wedge \gamma_{y}} + +\psi^{tz}\boldsymbol{\gamma_{t}\wedge \gamma_{z}} + +\psi^{xy}\boldsymbol{\gamma_{x}\wedge \gamma_{y}} + +\psi^{xz}\boldsymbol{\gamma_{x}\wedge \gamma_{z}} + +\psi^{yz}\boldsymbol{\gamma_{y}\wedge \gamma_{z}} \\ + &+\psi^{txyz}\boldsymbol{\gamma_{t}\wedge \gamma_{x}\wedge \gamma_{y}\wedge \gamma_{z}} \\ + \nabla \boldsymbol{\psi} I \sigma_{z}-e\boldsymbol{A}\boldsymbol{\psi}-m\boldsymbol{\psi}\gamma_{t} = 0 = & \left ( - e A^{t} \psi - e A^{x} \psi^{tx} - e A^{y} \psi^{ty} - e A^{z} \psi^{tz} - m \psi - \partial_{y} \psi^{tx} - \partial_{z} \psi^{txyz} + \partial_{x} \psi^{ty} + \partial_{t} \psi^{xy}\right ) \boldsymbol{\gamma_{t}} \\ + \hspace{-0.5in}& +\left ( - e A^{t} \psi^{tx} - e A^{x} \psi - e A^{y} \psi^{xy} - e A^{z} \psi^{xz} + m \psi^{tx} + \partial_{y} \psi - \partial_{t} \psi^{ty} - \partial_{x} \psi^{xy} + \partial_{z} \psi^{yz}\right ) \boldsymbol{\gamma_{x}} \\ + \hspace{-0.5in}& +\left ( - e A^{t} \psi^{ty} + e A^{x} \psi^{xy} - e A^{y} \psi - e A^{z} \psi^{yz} + m \psi^{ty} - \partial_{x} \psi + \partial_{t} \psi^{tx} - \partial_{y} \psi^{xy} - \partial_{z} \psi^{xz}\right ) \boldsymbol{\gamma_{y}} \\ + \hspace{-0.5in}& +\left ( - e A^{t} \psi^{tz} + e A^{x} \psi^{xz} + e A^{y} \psi^{yz} - e A^{z} \psi + m \psi^{tz} + \partial_{t} \psi^{txyz} - \partial_{z} \psi^{xy} + \partial_{y} \psi^{xz} - \partial_{x} \psi^{yz}\right ) \boldsymbol{\gamma_{z}} \\ + \hspace{-0.5in}& +\left ( - e A^{t} \psi^{xy} + e A^{x} \psi^{ty} - e A^{y} \psi^{tx} - e A^{z} \psi^{txyz} - m \psi^{xy} - \partial_{t} \psi + \partial_{x} \psi^{tx} + \partial_{y} \psi^{ty} + \partial_{z} \psi^{tz}\right ) \boldsymbol{\gamma_{t}\wedge \gamma_{x}\wedge \gamma_{y}} \\ + \hspace{-0.5in}& +\left ( - e A^{t} \psi^{xz} + e A^{x} \psi^{tz} + e A^{y} \psi^{txyz} - e A^{z} \psi^{tx} - m \psi^{xz} + \partial_{x} \psi^{txyz} + \partial_{z} \psi^{ty} - \partial_{y} \psi^{tz} - \partial_{t} \psi^{yz}\right ) \boldsymbol{\gamma_{t}\wedge \gamma_{x}\wedge \gamma_{z}} \\ + \hspace{-0.5in}& +\left ( - e A^{t} \psi^{yz} - e A^{x} \psi^{txyz} + e A^{y} \psi^{tz} - e A^{z} \psi^{ty} - m \psi^{yz} - \partial_{z} \psi^{tx} + \partial_{y} \psi^{txyz} + \partial_{x} \psi^{tz} + \partial_{t} \psi^{xz}\right ) \boldsymbol{\gamma_{t}\wedge \gamma_{y}\wedge \gamma_{z}} \\ + \hspace{-0.5in}& +\left ( - e A^{t} \psi^{txyz} - e A^{x} \psi^{yz} + e A^{y} \psi^{xz} - e A^{z} \psi^{xy} + m \psi^{txyz} + \partial_{z} \psi - \partial_{t} \psi^{tz} - \partial_{x} \psi^{xz} - \partial_{y} \psi^{yz}\right ) \boldsymbol{\gamma_{x}\wedge \gamma_{y}\wedge \gamma_{z}} + \end{align*} + + + +.. [Doran] ``_ + ``Geometric Algebra for Physicists`` by C. Doran and A. Lasenby, Cambridge + University Press, 2003. + + +.. [Hestenes] ``_ + ``Clifford Algebra to Geometric Calculus`` by D.Hestenes and G. Sobczyk, Kluwer + Academic Publishers, 1984. + + +.. [Macdonald] ''_ + ``Linear and Geometric Algebra`` by Alan Macdonald, ``_ diff --git a/latest/_sources/modules/galgebra/debug.txt b/latest/_sources/modules/galgebra/debug.txt new file mode 100644 index 000000000000..07ed89f987e7 --- /dev/null +++ b/latest/_sources/modules/galgebra/debug.txt @@ -0,0 +1,15 @@ +Debug code for Geometric Algebra +================================ + +.. module:: sympy.galgebra.debug + +Function Reference +------------------ + +.. autofunction:: ostr + +.. autofunction:: oprint + +.. autofunction:: print_product_table + +.. autofunction:: print_sub_table diff --git a/latest/_sources/modules/galgebra/index.txt b/latest/_sources/modules/galgebra/index.txt new file mode 100644 index 000000000000..9384faea62e7 --- /dev/null +++ b/latest/_sources/modules/galgebra/index.txt @@ -0,0 +1,21 @@ +======================== +Geometric Algebra Module +======================== + +.. automodule:: sympy.galgebra + +Documentation for Geometric Algebra module. + +Contents: + +.. toctree:: + :maxdepth: 2 + + ga.rst + manifold.rst + vector.rst + precedence.rst + printing.rst + ncutil.rst + stringarrays.rst + debug.rst diff --git a/latest/_sources/modules/galgebra/manifold.txt b/latest/_sources/modules/galgebra/manifold.txt new file mode 100644 index 000000000000..ba445b6706ec --- /dev/null +++ b/latest/_sources/modules/galgebra/manifold.txt @@ -0,0 +1,10 @@ +Manifold for Geometric Algebra +============================== + +.. module:: sympy.galgebra.manifold + +Class Reference +--------------- + +.. autoclass:: Manifold + :members: diff --git a/latest/_sources/modules/galgebra/ncutil.txt b/latest/_sources/modules/galgebra/ncutil.txt new file mode 100644 index 000000000000..2a524c1518dd --- /dev/null +++ b/latest/_sources/modules/galgebra/ncutil.txt @@ -0,0 +1,31 @@ +Noncommutative utilities for Geometric Algebra +============================================== + +.. module:: sympy.galgebra.ncutil + +Function Reference +------------------ + +.. autofunction:: bilinear_function + +.. autofunction:: bilinear_product + +.. autofunction:: coef_function + +.. autofunction:: linear_derivation + +.. autofunction:: linear_expand + +.. autofunction:: linear_function + +.. autofunction:: linear_projection + +.. autofunction:: multilinear_derivation + +.. autofunction:: multilinear_function + +.. autofunction:: multilinear_product + +.. autofunction:: non_scalar_projection + +.. autofunction:: product_derivation diff --git a/latest/_sources/modules/galgebra/precedence.txt b/latest/_sources/modules/galgebra/precedence.txt new file mode 100644 index 000000000000..cd4409454a12 --- /dev/null +++ b/latest/_sources/modules/galgebra/precedence.txt @@ -0,0 +1,23 @@ +Precedence for Geometric Algebra +================================ + +.. module:: sympy.galgebra.precedence + +Function Reference +------------------ + +.. autofunction:: add_paren + +.. autofunction:: contains_interval + +.. autofunction:: define_precedence + +.. autofunction:: GAeval + +.. autofunction:: parse_line + +.. autofunction:: parse_paren + +.. autofunction:: sub_paren + +.. autofunction:: unparse_paren diff --git a/latest/_sources/modules/galgebra/printing.txt b/latest/_sources/modules/galgebra/printing.txt new file mode 100644 index 000000000000..026b3c4c372d --- /dev/null +++ b/latest/_sources/modules/galgebra/printing.txt @@ -0,0 +1,31 @@ +Printing for Geometric Algebra +============================== + +.. module:: sympy.galgebra.printing + +Class Reference +--------------- + +.. autoclass:: enhance_print + :members: + +.. autoclass:: GA_Printer + :members: + +.. autoclass:: GA_LatexPrinter + :members: + +Function Reference +------------------ + +.. autofunction:: find_executable + +.. autofunction:: Get_Program + +.. autofunction:: latex + +.. autofunction:: Print_Function + +.. autofunction:: print_latex + +.. autofunction:: xdvi diff --git a/latest/_sources/modules/galgebra/stringarrays.txt b/latest/_sources/modules/galgebra/stringarrays.txt new file mode 100644 index 000000000000..e9c4b43fc70e --- /dev/null +++ b/latest/_sources/modules/galgebra/stringarrays.txt @@ -0,0 +1,15 @@ +String manipulation for Geometric Algebra +========================================= + +.. module:: sympy.galgebra.stringarrays + +Function Reference +------------------ + +.. autofunction:: fct_sym_array + +.. autofunction:: str_array + +.. autofunction:: str_combinations + +.. autofunction:: symbol_array diff --git a/latest/_sources/modules/galgebra/vector.txt b/latest/_sources/modules/galgebra/vector.txt new file mode 100644 index 000000000000..f0a768bbee54 --- /dev/null +++ b/latest/_sources/modules/galgebra/vector.txt @@ -0,0 +1,17 @@ +Vector for Geometric Algebra +============================ + +.. module:: sympy.galgebra.vector + +Class Reference +--------------- + +.. autoclass:: Vector + :members: + +Function Reference +------------------ + +.. autofunction:: flatten + +.. autofunction:: TrigSimp diff --git a/latest/_sources/modules/geometry/point3d.txt b/latest/_sources/modules/geometry/point3d.txt new file mode 100644 index 000000000000..221463cb4fa9 --- /dev/null +++ b/latest/_sources/modules/geometry/point3d.txt @@ -0,0 +1,5 @@ +3D Point +-------- + +.. automodule:: sympy.geometry.point3d + :members: diff --git a/latest/_sources/modules/mpmath/basics.txt b/latest/_sources/modules/mpmath/basics.txt new file mode 100644 index 000000000000..312527e1defe --- /dev/null +++ b/latest/_sources/modules/mpmath/basics.txt @@ -0,0 +1,227 @@ +Basic usage +=========================== + +In interactive code examples that follow, it will be assumed that +all items in the ``mpmath`` namespace have been imported:: + + >>> from mpmath import * + +Importing everything can be convenient, especially when using mpmath interactively, but be +careful when mixing mpmath with other libraries! To avoid inadvertently overriding +other functions or objects, explicitly import only the needed objects, or use +the ``mpmath.`` or ``mp.`` namespaces:: + + from mpmath import sin, cos + sin(1), cos(1) + + import mpmath + mpmath.sin(1), mpmath.cos(1) + + from mpmath import mp # mp context object -- to be explained + mp.sin(1), mp.cos(1) + + +Number types +------------ + +Mpmath provides the following numerical types: + + +------------+----------------+ + | Class | Description | + +============+================+ + | ``mpf`` | Real float | + +------------+----------------+ + | ``mpc`` | Complex float | + +------------+----------------+ + | ``matrix`` | Matrix | + +------------+----------------+ + +The following section will provide a very short introduction to the types ``mpf`` and ``mpc``. Intervals and matrices are described further in the documentation chapters on interval arithmetic and matrices / linear algebra. + +The ``mpf`` type is analogous to Python's built-in ``float``. It holds a real number or one of the special values ``inf`` (positive infinity), ``-inf`` (negative infinity) and ``nan`` (not-a-number, indicating an indeterminate result). You can create ``mpf`` instances from strings, integers, floats, and other ``mpf`` instances: + + >>> mpf(4) + mpf('4.0') + >>> mpf(2.5) + mpf('2.5') + >>> mpf("1.25e6") + mpf('1250000.0') + >>> mpf(mpf(2)) + mpf('2.0') + >>> mpf("inf") + mpf('+inf') + +The ``mpc`` type represents a complex number in rectangular form as a pair of ``mpf`` instances. It can be constructed from a Python ``complex``, a real number, or a pair of real numbers: + + >>> mpc(2,3) + mpc(real='2.0', imag='3.0') + >>> mpc(complex(2,3)).imag + mpf('3.0') + +You can mix ``mpf`` and ``mpc`` instances with each other and with Python numbers: + + >>> mpf(3) + 2*mpf('2.5') + 1.0 + mpf('9.0') + >>> mp.dps = 15 # Set precision (see below) + >>> mpc(1j)**0.5 + mpc(real='0.70710678118654757', imag='0.70710678118654757') + + +Setting the precision +--------------------- + +Mpmath uses a global working precision; it does not keep track of the precision or accuracy of individual numbers. Performing an arithmetic operation or calling ``mpf()`` rounds the result to the current working precision. The working precision is controlled by a context object called ``mp``, which has the following default state: + + >>> print mp + Mpmath settings: + mp.prec = 53 [default: 53] + mp.dps = 15 [default: 15] + mp.trap_complex = False [default: False] + +The term **prec** denotes the binary precision (measured in bits) while **dps** (short for *decimal places*) is the decimal precision. Binary and decimal precision are related roughly according to the formula ``prec = 3.33*dps``. For example, it takes a precision of roughly 333 bits to hold an approximation of pi that is accurate to 100 decimal places (actually slightly more than 333 bits is used). + +Changing either precision property of the ``mp`` object automatically updates the other; usually you just want to change the ``dps`` value: + + >>> mp.dps = 100 + >>> mp.dps + 100 + >>> mp.prec + 336 + +When the precision has been set, all ``mpf`` operations are carried out at that precision:: + + >>> mp.dps = 50 + >>> mpf(1) / 6 + mpf('0.16666666666666666666666666666666666666666666666666656') + >>> mp.dps = 25 + >>> mpf(2) ** mpf('0.5') + mpf('1.414213562373095048801688713') + +The precision of complex arithmetic is also controlled by the ``mp`` object: + + >>> mp.dps = 10 + >>> mpc(1,2) / 3 + mpc(real='0.3333333333321', imag='0.6666666666642') + +There is no restriction on the magnitude of numbers. An ``mpf`` can for example hold an approximation of a large Mersenne prime: + + >>> mp.dps = 15 + >>> print mpf(2)**32582657 - 1 + 1.24575026015369e+9808357 + +Or why not 1 googolplex: + + >>> print mpf(10) ** (10**100) # doctest:+ELLIPSIS + 1.0e+100000000000000000000000000000000000000000000000000... + +The (binary) exponent is stored exactly and is independent of the precision. + +Temporarily changing the precision +.................................. + +It is often useful to change the precision during only part of a calculation. A way to temporarily increase the precision and then restore it is as follows: + + >>> mp.prec += 2 + >>> # do_something() + >>> mp.prec -= 2 + +In Python 2.5, the ``with`` statement along with the mpmath functions ``workprec``, ``workdps``, ``extraprec`` and ``extradps`` can be used to temporarily change precision in a more safe manner: + + >>> from __future__ import with_statement + >>> with workdps(20): # doctest: +SKIP + ... print mpf(1)/7 + ... with extradps(10): + ... print mpf(1)/7 + ... + 0.14285714285714285714 + 0.142857142857142857142857142857 + >>> mp.dps + 15 + +The ``with`` statement ensures that the precision gets reset when exiting the block, even in the case that an exception is raised. (The effect of the ``with`` statement can be emulated in Python 2.4 by using a ``try/finally`` block.) + +The ``workprec`` family of functions can also be used as function decorators: + + >>> @workdps(6) + ... def f(): + ... return mpf(1)/3 + ... + >>> f() + mpf('0.33333331346511841') + + +Some functions accept the ``prec`` and ``dps`` keyword arguments and this will override the global working precision. Note that this will not affect the precision at which the result is printed, so to get all digits, you must either use increase precision afterward when printing or use ``nstr``/``nprint``: + + >>> mp.dps = 15 + >>> print exp(1) + 2.71828182845905 + >>> print exp(1, dps=50) # Extra digits won't be printed + 2.71828182845905 + >>> nprint(exp(1, dps=50), 50) + 2.7182818284590452353602874713526624977572470937 + +Finally, instead of using the global context object ``mp``, you can create custom contexts and work with methods of those instances instead of global functions. The working precision will be local to each context object: + + >>> mp2 = mp.clone() + >>> mp.dps = 10 + >>> mp2.dps = 20 + >>> print mp.mpf(1) / 3 + 0.3333333333 + >>> print mp2.mpf(1) / 3 + 0.33333333333333333333 + +**Note**: the ability to create multiple contexts is a new feature that is only partially implemented. Not all mpmath functions are yet available as context-local methods. In the present version, you are likely to encounter bugs if you try mixing different contexts. + +Providing correct input +----------------------- + +Note that when creating a new ``mpf``, the value will at most be as accurate as the input. *Be careful when mixing mpmath numbers with Python floats*. When working at high precision, fractional ``mpf`` values should be created from strings or integers: + + >>> mp.dps = 30 + >>> mpf(10.9) # bad + mpf('10.9000000000000003552713678800501') + >>> mpf('10.9') # good + mpf('10.8999999999999999999999999999997') + >>> mpf(109) / mpf(10) # also good + mpf('10.8999999999999999999999999999997') + >>> mp.dps = 15 + +(Binary fractions such as 0.5, 1.5, 0.75, 0.125, etc, are generally safe as input, however, since those can be represented exactly by Python floats.) + +Printing +-------- + +By default, the ``repr()`` of a number includes its type signature. This way ``eval`` can be used to recreate a number from its string representation: + + >>> eval(repr(mpf(2.5))) + mpf('2.5') + +Prettier output can be obtained by using ``str()`` or ``print``, which hide the ``mpf`` and ``mpc`` signatures and also suppress rounding artifacts in the last few digits: + + >>> mpf("3.14159") + mpf('3.1415899999999999') + >>> print mpf("3.14159") + 3.14159 + >>> print mpc(1j)**0.5 + (0.707106781186548 + 0.707106781186548j) + +Setting the ``mp.pretty`` option will use the ``str()``-style output for ``repr()`` as well: + + >>> mp.pretty = True + >>> mpf(0.6) + 0.6 + >>> mp.pretty = False + >>> mpf(0.6) + mpf('0.59999999999999998') + +The number of digits with which numbers are printed by default is determined by the working precision. To specify the number of digits to show without changing the working precision, use :func:`mpmath.nstr` and :func:`mpmath.nprint`: + + >>> a = mpf(1) / 6 + >>> a + mpf('0.16666666666666666') + >>> nstr(a, 8) + '0.16666667' + >>> nprint(a, 8) + 0.16666667 + >>> nstr(a, 50) + '0.16666666666666665741480812812369549646973609924316' diff --git a/latest/_sources/modules/mpmath/calculus/approximation.txt b/latest/_sources/modules/mpmath/calculus/approximation.txt new file mode 100644 index 000000000000..e926426c20f5 --- /dev/null +++ b/latest/_sources/modules/mpmath/calculus/approximation.txt @@ -0,0 +1,23 @@ +Function approximation +---------------------- + +Taylor series (``taylor``) +.......................... + +.. autofunction:: mpmath.taylor + +Pade approximation (``pade``) +............................. + +.. autofunction:: mpmath.pade + +Chebyshev approximation (``chebyfit``) +...................................... + +.. autofunction:: mpmath.chebyfit + +Fourier series (``fourier``, ``fourierval``) +............................................ + +.. autofunction:: mpmath.fourier +.. autofunction:: mpmath.fourierval diff --git a/latest/_sources/modules/mpmath/calculus/differentiation.txt b/latest/_sources/modules/mpmath/calculus/differentiation.txt new file mode 100644 index 000000000000..bccc7449faab --- /dev/null +++ b/latest/_sources/modules/mpmath/calculus/differentiation.txt @@ -0,0 +1,19 @@ +Differentiation +--------------- + +Numerical derivatives (``diff``, ``diffs``) +........................................... + +.. autofunction:: mpmath.diff +.. autofunction:: mpmath.diffs + +Composition of derivatives (``diffs_prod``, ``diffs_exp``) +.......................................................... + +.. autofunction:: mpmath.diffs_prod +.. autofunction:: mpmath.diffs_exp + +Fractional derivatives / differintegration (``differint``) +............................................................ + +.. autofunction:: mpmath.differint diff --git a/latest/_sources/modules/mpmath/calculus/index.txt b/latest/_sources/modules/mpmath/calculus/index.txt new file mode 100644 index 000000000000..57ebd2c20feb --- /dev/null +++ b/latest/_sources/modules/mpmath/calculus/index.txt @@ -0,0 +1,13 @@ +Numerical calculus +================== + +.. toctree:: + :maxdepth: 2 + + polynomials.rst + optimization.rst + sums_limits.rst + differentiation.rst + integration.rst + odes.rst + approximation.rst diff --git a/latest/_sources/modules/mpmath/calculus/integration.txt b/latest/_sources/modules/mpmath/calculus/integration.txt new file mode 100644 index 000000000000..f4aac260c837 --- /dev/null +++ b/latest/_sources/modules/mpmath/calculus/integration.txt @@ -0,0 +1,31 @@ +Numerical integration (quadrature) +---------------------------------- + +Standard quadrature (``quad``) +.............................. + +.. autofunction:: mpmath.quad + +Oscillatory quadrature (``quadosc``) +.................................... + +.. autofunction:: mpmath.quadosc + +Quadrature rules +................ + +.. autoclass:: mpmath.calculus.quadrature.QuadratureRule + :members: + +Tanh-sinh rule +~~~~~~~~~~~~~~ + +.. autoclass:: mpmath.calculus.quadrature.TanhSinh + :members: + + +Gauss-Legendre rule +~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: mpmath.calculus.quadrature.GaussLegendre + :members: diff --git a/latest/_sources/modules/mpmath/calculus/odes.txt b/latest/_sources/modules/mpmath/calculus/odes.txt new file mode 100644 index 000000000000..83e1cfb36f24 --- /dev/null +++ b/latest/_sources/modules/mpmath/calculus/odes.txt @@ -0,0 +1,7 @@ +Ordinary differential equations +------------------------------- + +Solving the ODE initial value problem (``odefun``) +.................................................. + +.. autofunction:: mpmath.odefun diff --git a/latest/_sources/modules/mpmath/calculus/optimization.txt b/latest/_sources/modules/mpmath/calculus/optimization.txt new file mode 100644 index 000000000000..59ab395903be --- /dev/null +++ b/latest/_sources/modules/mpmath/calculus/optimization.txt @@ -0,0 +1,29 @@ +Root-finding and optimization +----------------------------- + +Root-finding (``findroot``) +........................... + +.. autofunction:: mpmath.findroot(f, x0, solver=Secant, tol=None, verbose=False, verify=True, **kwargs) + +Solvers +^^^^^^^ + +.. autoclass:: mpmath.calculus.optimization.Secant +.. autoclass:: mpmath.calculus.optimization.Newton +.. autoclass:: mpmath.calculus.optimization.MNewton +.. autoclass:: mpmath.calculus.optimization.Halley +.. autoclass:: mpmath.calculus.optimization.Muller +.. autoclass:: mpmath.calculus.optimization.Bisection +.. autoclass:: mpmath.calculus.optimization.Illinois +.. autoclass:: mpmath.calculus.optimization.Pegasus +.. autoclass:: mpmath.calculus.optimization.Anderson +.. autoclass:: mpmath.calculus.optimization.Ridder +.. autoclass:: mpmath.calculus.optimization.ANewton +.. autoclass:: mpmath.calculus.optimization.MDNewton + + +.. Minimization and maximization (``findmin``, ``findmax``) +.. ........................................................ + +.. (To be added.) diff --git a/latest/_sources/modules/mpmath/calculus/polynomials.txt b/latest/_sources/modules/mpmath/calculus/polynomials.txt new file mode 100644 index 000000000000..638316a84768 --- /dev/null +++ b/latest/_sources/modules/mpmath/calculus/polynomials.txt @@ -0,0 +1,15 @@ +Polynomials +----------- + +See also :func:`taylor` and :func:`chebyfit` for +approximation of functions by polynomials. + +Polynomial evaluation (``polyval``) +................................... + +.. autofunction:: mpmath.polyval + +Polynomial roots (``polyroots``) +................................ + +.. autofunction:: mpmath.polyroots diff --git a/latest/_sources/modules/mpmath/calculus/sums_limits.txt b/latest/_sources/modules/mpmath/calculus/sums_limits.txt new file mode 100644 index 000000000000..f697a625e214 --- /dev/null +++ b/latest/_sources/modules/mpmath/calculus/sums_limits.txt @@ -0,0 +1,67 @@ +Sums, products, limits and extrapolation +---------------------------------------- + +The functions listed here permit approximation of infinite +sums, products, and other sequence limits. +Use :func:`mpmath.fsum` and :func:`mpmath.fprod` +for summation and multiplication of finite sequences. + +Summation +.......................................... + +:func:`nsum` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.nsum + +:func:`sumem` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.sumem + +:func:`sumap` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.sumap + +Products +............................... + +:func:`nprod` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.nprod + +Limits (``limit``) +.................. + +:func:`limit` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.limit + +Extrapolation +.......................................... + +The following functions provide a direct interface to +extrapolation algorithms. :func:`nsum` and :func:`limit` essentially +work by calling the following functions with an increasing +number of terms until the extrapolated limit is accurate enough. + +The following functions may be useful to call directly if the +precise number of terms needed to achieve a desired accuracy is +known in advance, or if one wishes to study the convergence +properties of the algorithms. + + +:func:`richardson` +^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.richardson + +:func:`shanks` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.shanks + +:func:`levin` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.levin + +:func:`cohen_alt` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.cohen_alt + diff --git a/latest/_sources/modules/mpmath/contexts.txt b/latest/_sources/modules/mpmath/contexts.txt new file mode 100644 index 000000000000..d454290206d3 --- /dev/null +++ b/latest/_sources/modules/mpmath/contexts.txt @@ -0,0 +1,306 @@ +Contexts +======== + +High-level code in mpmath is implemented as methods on a "context object". The context implements arithmetic, type conversions and other fundamental operations. The context also holds settings such as precision, and stores cache data. A few different contexts (with a mostly compatible interface) are provided so that the high-level algorithms can be used with different implementations of the underlying arithmetic, allowing different features and speed-accuracy tradeoffs. Currently, mpmath provides the following contexts: + + * Arbitrary-precision arithmetic (``mp``) + * A faster Cython-based version of ``mp`` (used by default in Sage, and currently only available there) + * Arbitrary-precision interval arithmetic (``iv``) + * Double-precision arithmetic using Python's builtin ``float`` and ``complex`` types (``fp``) + +Most global functions in the global mpmath namespace are actually methods of the ``mp`` +context. This fact is usually transparent to the user, but sometimes shows up in the +form of an initial parameter called "ctx" visible in the help for the function:: + + >>> import mpmath + >>> help(mpmath.fsum) # doctest:+SKIP + Help on method fsum in module mpmath.ctx_mp_python: + + fsum(ctx, terms, absolute=False, squared=False) method of mpmath.ctx_mp.MPContext instance + Calculates a sum containing a finite number of terms (for infinite + series, see :func:`~mpmath.nsum`). The terms will be converted to + ... + +The following operations are equivalent:: + + >>> mpmath.mp.dps = 15; mpmath.mp.pretty = False + >>> mpmath.fsum([1,2,3]) + mpf('6.0') + >>> mpmath.mp.fsum([1,2,3]) + mpf('6.0') + +The corresponding operation using the ``fp`` context:: + + >>> mpmath.fp.fsum([1,2,3]) + 6.0 + +Common interface +---------------- + +``ctx.mpf`` creates a real number:: + + >>> from mpmath import mp, fp + >>> mp.mpf(3) + mpf('3.0') + >>> fp.mpf(3) + 3.0 + +``ctx.mpc`` creates a complex number:: + + >>> mp.mpc(2,3) + mpc(real='2.0', imag='3.0') + >>> fp.mpc(2,3) + (2+3j) + +``ctx.matrix`` creates a matrix:: + + >>> mp.matrix([[1,0],[0,1]]) + matrix( + [['1.0', '0.0'], + ['0.0', '1.0']]) + >>> _[0,0] + mpf('1.0') + >>> fp.matrix([[1,0],[0,1]]) + matrix( + [['1.0', '0.0'], + ['0.0', '1.0']]) + >>> _[0,0] + 1.0 + +``ctx.prec`` holds the current precision (in bits):: + + >>> mp.prec + 53 + >>> fp.prec + 53 + +``ctx.dps`` holds the current precision (in digits):: + + >>> mp.dps + 15 + >>> fp.dps + 15 + +``ctx.pretty`` controls whether objects should be pretty-printed automatically by :func:`repr`. Pretty-printing for ``mp`` numbers is disabled by default so that they can clearly be distinguished from Python numbers and so that ``eval(repr(x)) == x`` works:: + + >>> mp.mpf(3) + mpf('3.0') + >>> mpf = mp.mpf + >>> eval(repr(mp.mpf(3))) + mpf('3.0') + >>> mp.pretty = True + >>> mp.mpf(3) + 3.0 + >>> fp.matrix([[1,0],[0,1]]) + matrix( + [['1.0', '0.0'], + ['0.0', '1.0']]) + >>> fp.pretty = True + >>> fp.matrix([[1,0],[0,1]]) + [1.0 0.0] + [0.0 1.0] + >>> fp.pretty = False + >>> mp.pretty = False + + +Arbitrary-precision floating-point (``mp``) +--------------------------------------------- + +The ``mp`` context is what most users probably want to use most of the time, as it supports the most functions, is most well-tested, and is implemented with a high level of optimization. Nearly all examples in this documentation use ``mp`` functions. + +See :doc:`basics` for a description of basic usage. + +Arbitrary-precision interval arithmetic (``iv``) +------------------------------------------------ + +The ``iv.mpf`` type represents a closed interval `[a,b]`; that is, the set `\{x : a \le x \le b\}`, where `a` and `b` are arbitrary-precision floating-point values, possibly `\pm \infty`. The ``iv.mpc`` type represents a rectangular complex interval `[a,b] + [c,d]i`; that is, the set `\{z = x+iy : a \le x \le b \land c \le y \le d\}`. + +Interval arithmetic provides rigorous error tracking. If `f` is a mathematical function and `\hat f` is its interval arithmetic version, then the basic guarantee of interval arithmetic is that `f(v) \subseteq \hat f(v)` for any input interval `v`. Put differently, if an interval represents the known uncertainty for a fixed number, any sequence of interval operations will produce an interval that contains what would be the result of applying the same sequence of operations to the exact number. The principal drawbacks of interval arithmetic are speed (``iv`` arithmetic is typically at least two times slower than ``mp`` arithmetic) and that it sometimes provides far too pessimistic bounds. + +.. note :: + + The support for interval arithmetic in mpmath is still experimental, and many functions + do not yet properly support intervals. Please use this feature with caution. + +Intervals can be created from single numbers (treated as zero-width intervals) or pairs of endpoint numbers. Strings are treated as exact decimal numbers. Note that a Python float like ``0.1`` generally does not represent the same number as its literal; use ``'0.1'`` instead:: + + >>> from mpmath import iv + >>> iv.dps = 15; iv.pretty = False + >>> iv.mpf(3) + mpi('3.0', '3.0') + >>> print iv.mpf(3) + [3.0, 3.0] + >>> iv.pretty = True + >>> iv.mpf([2,3]) + [2.0, 3.0] + >>> iv.mpf(0.1) # probably not intended + [0.10000000000000000555, 0.10000000000000000555] + >>> iv.mpf('0.1') # good, gives a containing interval + [0.099999999999999991673, 0.10000000000000000555] + >>> iv.mpf(['0.1', '0.2']) + [0.099999999999999991673, 0.2000000000000000111] + +The fact that ``'0.1'`` results in an interval of nonzero width indicates that 1/10 cannot be represented using binary floating-point numbers at this precision level (in fact, it cannot be represented exactly at any precision). + +Intervals may be infinite or half-infinite:: + + >>> print 1 / iv.mpf([2, 'inf']) + [0.0, 0.5] + +The equality testing operators ``==`` and ``!=`` check whether their operands are identical as intervals; that is, have the same endpoints. The ordering operators ``< <= > >=`` permit inequality testing using triple-valued logic: a guaranteed inequality returns ``True`` or ``False`` while an indeterminate inequality returns ``None``:: + + >>> iv.mpf([1,2]) == iv.mpf([1,2]) + True + >>> iv.mpf([1,2]) != iv.mpf([1,2]) + False + >>> iv.mpf([1,2]) <= 2 + True + >>> iv.mpf([1,2]) > 0 + True + >>> iv.mpf([1,2]) < 1 + False + >>> iv.mpf([1,2]) < 2 # returns None + >>> iv.mpf([2,2]) < 2 + False + >>> iv.mpf([1,2]) <= iv.mpf([2,3]) + True + >>> iv.mpf([1,2]) < iv.mpf([2,3]) # returns None + >>> iv.mpf([1,2]) < iv.mpf([-1,0]) + False + +The ``in`` operator tests whether a number or interval is contained in another interval:: + + >>> iv.mpf([0,2]) in iv.mpf([0,10]) + True + >>> 3 in iv.mpf(['-inf', 0]) + False + +Intervals have the properties ``.a``, ``.b`` (endpoints), ``.mid``, and ``.delta`` (width):: + + >>> x = iv.mpf([2, 5]) + >>> x.a + [2.0, 2.0] + >>> x.b + [5.0, 5.0] + >>> x.mid + [3.5, 3.5] + >>> x.delta + [3.0, 3.0] + +Some transcendental functions are supported:: + + >>> iv.dps = 15 + >>> mp.dps = 15 + >>> iv.mpf([0.5,1.5]) ** iv.mpf([0.5, 1.5]) + [0.35355339059327373086, 1.837117307087383633] + >>> iv.exp(0) + [1.0, 1.0] + >>> iv.exp(['-inf','inf']) + [0.0, +inf] + >>> + >>> iv.exp(['-inf',0]) + [0.0, 1.0] + >>> iv.exp([0,'inf']) + [1.0, +inf] + >>> iv.exp([0,1]) + [1.0, 2.7182818284590455349] + >>> + >>> iv.log(1) + [0.0, 0.0] + >>> iv.log([0,1]) + [-inf, 0.0] + >>> iv.log([0,'inf']) + [-inf, +inf] + >>> iv.log(2) + [0.69314718055994528623, 0.69314718055994539725] + >>> + >>> iv.sin([100,'inf']) + [-1.0, 1.0] + >>> iv.cos(['-0.1','0.1']) + [0.99500416527802570954, 1.0] + +Interval arithmetic is useful for proving inequalities involving irrational numbers. +Naive use of ``mp`` arithmetic may result in wrong conclusions, such as the following:: + + >>> mp.dps = 25 + >>> x = mp.exp(mp.pi*mp.sqrt(163)) + >>> y = mp.mpf(640320**3+744) + >>> print x + 262537412640768744.0000001 + >>> print y + 262537412640768744.0 + >>> x > y + True + +But the correct result is `e^{\pi \sqrt{163}} < 262537412640768744`, as can be +seen by increasing the precision:: + + >>> mp.dps = 50 + >>> print mp.exp(mp.pi*mp.sqrt(163)) + 262537412640768743.99999999999925007259719818568888 + +With interval arithmetic, the comparison returns ``None`` until the precision +is large enough for `x-y` to have a definite sign:: + + >>> iv.dps = 15 + >>> iv.exp(iv.pi*iv.sqrt(163)) > (640320**3+744) + >>> iv.dps = 30 + >>> iv.exp(iv.pi*iv.sqrt(163)) > (640320**3+744) + >>> iv.dps = 60 + >>> iv.exp(iv.pi*iv.sqrt(163)) > (640320**3+744) + False + >>> iv.dps = 15 + +Fast low-precision arithmetic (``fp``) +--------------------------------------------- + +Although mpmath is generally designed for arbitrary-precision arithmetic, many of the high-level algorithms work perfectly well with ordinary Python ``float`` and ``complex`` numbers, which use hardware double precision (on most systems, this corresponds to 53 bits of precision). Whereas the global functions (which are methods of the ``mp`` object) always convert inputs to mpmath numbers, the ``fp`` object instead converts them to ``float`` or ``complex``, and in some cases employs basic functions optimized for double precision. When large amounts of function evaluations (numerical integration, plotting, etc) are required, and when ``fp`` arithmetic provides sufficient accuracy, this can give a significant speedup over ``mp`` arithmetic. + +To take advantage of this feature, simply use the ``fp`` prefix, i.e. write ``fp.func`` instead of ``func`` or ``mp.func``:: + + >>> u = fp.erfc(2.5) + >>> print u + 0.000406952017445 + >>> type(u) + + >>> mp.dps = 15 + >>> print mp.erfc(2.5) + 0.000406952017444959 + >>> fp.matrix([[1,2],[3,4]]) ** 2 + matrix( + [['7.0', '10.0'], + ['15.0', '22.0']]) + >>> + >>> type(_[0,0]) + + >>> print fp.quad(fp.sin, [0, fp.pi]) # numerical integration + 2.0 + +The ``fp`` context wraps Python's ``math`` and ``cmath`` modules for elementary functions. It supports both real and complex numbers and automatically generates complex results for real inputs (``math`` raises an exception):: + + >>> fp.sqrt(5) + 2.23606797749979 + >>> fp.sqrt(-5) + 2.23606797749979j + >>> fp.sin(10) + -0.5440211108893698 + >>> fp.power(-1, 0.25) + (0.7071067811865476+0.7071067811865475j) + >>> (-1) ** 0.25 + Traceback (most recent call last): + ... + ValueError: negative number cannot be raised to a fractional power + +The ``prec`` and ``dps`` attributes can be changed (for interface compatibility with the ``mp`` context) but this has no effect:: + + >>> fp.prec + 53 + >>> fp.dps + 15 + >>> fp.prec = 80 + >>> fp.prec + 53 + >>> fp.dps + 15 + +Due to intermediate rounding and cancellation errors, results computed with ``fp`` arithmetic may be much less accurate than those computed with ``mp`` using an equivalent precision (``mp.prec = 53``), since the latter often uses increased internal precision. The accuracy is highly problem-dependent: for some functions, ``fp`` almost always gives 14-15 correct digits; for others, results can be accurate to only 2-3 digits or even completely wrong. The recommended use for ``fp`` is therefore to speed up large-scale computations where accuracy can be verified in advance on a subset of the input set, or where results can be verified afterwards. diff --git a/latest/_sources/modules/mpmath/functions/bessel.txt b/latest/_sources/modules/mpmath/functions/bessel.txt new file mode 100644 index 000000000000..c25480af891d --- /dev/null +++ b/latest/_sources/modules/mpmath/functions/bessel.txt @@ -0,0 +1,185 @@ +Bessel functions and related functions +-------------------------------------- + +The functions in this section arise as solutions to various differential equations in physics, typically describing wavelike oscillatory behavior or a combination of oscillation and exponential decay or growth. Mathematically, they are special cases of the confluent hypergeometric functions `\,_0F_1`, `\,_1F_1` and `\,_1F_2` (see :doc:`hypergeometric`). + + +Bessel functions +................................................... + +:func:`besselj` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. autofunction:: mpmath.besselj(n,x,derivative=0) +.. autofunction:: mpmath.j0(x) +.. autofunction:: mpmath.j1(x) + +:func:`bessely` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.bessely(n,x,derivative=0) + +:func:`besseli` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.besseli(n,x,derivative=0) + +:func:`besselk` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.besselk(n,x) + + +Bessel function zeros +............................... + +:func:`besseljzero` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.besseljzero(v,m,derivative=0) + +:func:`besselyzero` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.besselyzero(v,m,derivative=0) + + +Hankel functions +................ + +:func:`hankel1` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.hankel1(n,x) + +:func:`hankel2` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.hankel2(n,x) + + +Kelvin functions +................ + +:func:`ber` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.ber + +:func:`bei` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. autofunction:: mpmath.bei + +:func:`ker` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.ker + +:func:`kei` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.kei + + +Struve functions +................................................... + +:func:`struveh` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.struveh + +:func:`struvel` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.struvel + + +Anger-Weber functions +................................................... + +:func:`angerj` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.angerj + +:func:`webere` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.webere + + +Lommel functions +................................................... + +:func:`lommels1` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.lommels1 + +:func:`lommels2` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.lommels2 + +Airy and Scorer functions +............................................... + +:func:`airyai` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.airyai(z, derivative=0, **kwargs) + +:func:`airybi` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.airybi(z, derivative=0, **kwargs) + +:func:`airyaizero` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.airyaizero(k, derivative=0) + +:func:`airybizero` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.airybizero(k, derivative=0, complex=0) + +:func:`scorergi` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.scorergi(z, **kwargs) + +:func:`scorerhi` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.scorerhi(z, **kwargs) + + +Coulomb wave functions +............................................... + +:func:`coulombf` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.coulombf(l,eta,z) + +:func:`coulombg` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.coulombg(l,eta,z) + +:func:`coulombc` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.coulombc(l,eta) + +Confluent U and Whittaker functions +................................... + +:func:`hyperu` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.hyperu(a, b, z) + +:func:`whitm` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.whitm(k,m,z) + +:func:`whitw` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.whitw(k,m,z) + +Parabolic cylinder functions +................................. + +:func:`pcfd` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.pcfd(n,z,**kwargs) + +:func:`pcfu` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.pcfu(a,z,**kwargs) + +:func:`pcfv` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.pcfv(a,z,**kwargs) + +:func:`pcfw` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.pcfw(a,z,**kwargs) diff --git a/latest/_sources/modules/mpmath/functions/constants.txt b/latest/_sources/modules/mpmath/functions/constants.txt new file mode 100644 index 000000000000..51c99d576bac --- /dev/null +++ b/latest/_sources/modules/mpmath/functions/constants.txt @@ -0,0 +1,85 @@ +Mathematical constants +---------------------- + +Mpmath supports arbitrary-precision computation of various common (and less common) mathematical constants. These constants are implemented as lazy objects that can evaluate to any precision. Whenever the objects are used as function arguments or as operands in arithmetic operations, they automagically evaluate to the current working precision. A lazy number can be converted to a regular ``mpf`` using the unary ``+`` operator, or by calling it as a function:: + + >>> from mpmath import * + >>> mp.dps = 15 + >>> pi + + >>> 2*pi + mpf('6.2831853071795862') + >>> +pi + mpf('3.1415926535897931') + >>> pi() + mpf('3.1415926535897931') + >>> mp.dps = 40 + >>> pi + + >>> 2*pi + mpf('6.283185307179586476925286766559005768394338') + >>> +pi + mpf('3.141592653589793238462643383279502884197169') + >>> pi() + mpf('3.141592653589793238462643383279502884197169') + +Exact constants +............... + +The predefined objects :data:`j` (imaginary unit), :data:`inf` (positive infinity) and :data:`nan` (not-a-number) are shortcuts to :class:`mpc` and :class:`mpf` instances with these fixed values. + +Pi (``pi``) +.................................... + +.. autoattribute:: mpmath.mp.pi + +Degree (``degree``) +.................................... + +.. autoattribute:: mpmath.mp.degree + +Base of the natural logarithm (``e``) +..................................... + +.. autoattribute:: mpmath.mp.e + +Golden ratio (``phi``) +...................... + +.. autoattribute:: mpmath.mp.phi + + +Euler's constant (``euler``) +............................ + +.. autoattribute:: mpmath.mp.euler + +Catalan's constant (``catalan``) +................................ + +.. autoattribute:: mpmath.mp.catalan + +Apery's constant (``apery``) +............................ + +.. autoattribute:: mpmath.mp.apery + +Khinchin's constant (``khinchin``) +.................................. + +.. autoattribute:: mpmath.mp.khinchin + +Glaisher's constant (``glaisher``) +.................................. + +.. autoattribute:: mpmath.mp.glaisher + +Mertens constant (``mertens``) +.................................. + +.. autoattribute:: mpmath.mp.mertens + +Twin prime constant (``twinprime``) +................................... + +.. autoattribute:: mpmath.mp.twinprime diff --git a/latest/_sources/modules/mpmath/functions/elliptic.txt b/latest/_sources/modules/mpmath/functions/elliptic.txt new file mode 100644 index 000000000000..dc34e8817fb2 --- /dev/null +++ b/latest/_sources/modules/mpmath/functions/elliptic.txt @@ -0,0 +1,96 @@ +Elliptic functions +------------------ + +.. automodule :: mpmath.functions.elliptic + + +Elliptic arguments +................................................... + +:func:`qfrom` +^^^^^^^^^^^^^ +.. autofunction:: mpmath.qfrom(**kwargs) + +:func:`qbarfrom` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.qbarfrom(**kwargs) + +:func:`mfrom` +^^^^^^^^^^^^^ +.. autofunction:: mpmath.mfrom(**kwargs) + +:func:`kfrom` +^^^^^^^^^^^^^ +.. autofunction:: mpmath.kfrom(**kwargs) + +:func:`taufrom` +^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.taufrom(**kwargs) + + +Legendre elliptic integrals +................................................... + +:func:`ellipk` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.ellipk(m, **kwargs) + +:func:`ellipf` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.ellipf(phi, m) + +:func:`ellipe` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.ellipe(*args) + +:func:`ellippi` +^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.ellippi(*args) + + +Carlson symmetric elliptic integrals +................................................... + +:func:`elliprf` +^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.elliprf(x, y, z) + +:func:`elliprc` +^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.elliprc(x, y, pv=True) + +:func:`elliprj` +^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.elliprj(x, y, z, p) + +:func:`elliprd` +^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.elliprd(x, y, z) + +:func:`elliprg` +^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.elliprg(x, y, z) + + +Jacobi theta functions +...................... + +:func:`jtheta` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.jtheta(n,z,q,derivative=0) + + +Jacobi elliptic functions +................................................................. + +:func:`ellipfun` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.ellipfun(kind,u=None,m=None,q=None,k=None,tau=None) + + +Modular functions +...................... + +:func:`kleinj` +^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.kleinj(tau=None, **kwargs) diff --git a/latest/_sources/modules/mpmath/functions/expintegrals.txt b/latest/_sources/modules/mpmath/functions/expintegrals.txt new file mode 100644 index 000000000000..2be9e31bdae4 --- /dev/null +++ b/latest/_sources/modules/mpmath/functions/expintegrals.txt @@ -0,0 +1,104 @@ +Exponential integrals and error functions +----------------------------------------- + +Exponential integrals give closed-form solutions to a large class of commonly occurring transcendental integrals that cannot be evaluated using elementary functions. Integrals of this type include those with an integrand of the form `t^a e^{t}` or `e^{-x^2}`, the latter giving rise to the Gaussian (or normal) probability distribution. + +The most general function in this section is the incomplete gamma function, to which all others can be reduced. The incomplete gamma function, in turn, can be expressed using hypergeometric functions (see :doc:`hypergeometric`). + +Incomplete gamma functions +.......................... + +:func:`gammainc` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.gammainc(z, a=0, b=inf, regularized=False) + + +Exponential integrals +..................... + +:func:`ei` +^^^^^^^^^^ +.. autofunction:: mpmath.ei(x, **kwargs) + +:func:`e1` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.e1(x, **kwargs) + +:func:`expint` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.expint(*args) + + +Logarithmic integral +.................... + +:func:`li` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.li(x, **kwargs) + + +Trigonometric integrals +....................... + +:func:`ci` +^^^^^^^^^^ +.. autofunction:: mpmath.ci(x, **kwargs) + +:func:`si` +^^^^^^^^^^ +.. autofunction:: mpmath.si(x, **kwargs) + + +Hyperbolic integrals +.................... + +:func:`chi` +^^^^^^^^^^^ +.. autofunction:: mpmath.chi(x, **kwargs) + +:func:`shi` +^^^^^^^^^^^ +.. autofunction:: mpmath.shi(x, **kwargs) + + +Error functions +............... + +:func:`erf` +^^^^^^^^^^^ +.. autofunction:: mpmath.erf(x, **kwargs) + +:func:`erfc` +^^^^^^^^^^^^ +.. autofunction:: mpmath.erfc(x, **kwargs) + +:func:`erfi` +^^^^^^^^^^^^ +.. autofunction:: mpmath.erfi(x) + +:func:`erfinv` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.erfinv(x) + +The normal distribution +.................................................... + +:func:`npdf` +^^^^^^^^^^^^ +.. autofunction:: mpmath.npdf(x, mu=0, sigma=1) + +:func:`ncdf` +^^^^^^^^^^^^ +.. autofunction:: mpmath.ncdf(x, mu=0, sigma=1) + + +Fresnel integrals +...................................................... + +:func:`fresnels` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.fresnels(x) + +:func:`fresnelc` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.fresnelc(x) diff --git a/latest/_sources/modules/mpmath/functions/gamma.txt b/latest/_sources/modules/mpmath/functions/gamma.txt new file mode 100644 index 000000000000..afb1201837c9 --- /dev/null +++ b/latest/_sources/modules/mpmath/functions/gamma.txt @@ -0,0 +1,112 @@ +Factorials and gamma functions +------------------------------ + +Factorials and factorial-like sums and products are basic tools of combinatorics and number theory. Much like the exponential function is fundamental to differential equations and analysis in general, the factorial function (and its extension to complex numbers, the gamma function) is fundamental to difference equations and functional equations. + +A large selection of factorial-like functions is implemented in mpmath. All functions support complex arguments, and arguments may be arbitrarily large. Results are numerical approximations, so to compute *exact* values a high enough precision must be set manually:: + + >>> mp.dps = 15; mp.pretty = True + >>> fac(100) + 9.33262154439442e+157 + >>> print int(_) # most digits are wrong + 93326215443944150965646704795953882578400970373184098831012889540582227238570431 + 295066113089288327277825849664006524270554535976289719382852181865895959724032 + >>> mp.dps = 160 + >>> fac(100) + 93326215443944152681699238856266700490715968264381621468592963895217599993229915 + 608941463976156518286253697920827223758251185210916864000000000000000000000000.0 + +The gamma and polygamma functions are closely related to :doc:`zeta`. See also :doc:`qfunctions` for q-analogs of factorial-like functions. + + +Factorials +.......... + +:func:`factorial`/:func:`fac` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. autofunction:: mpmath.factorial(x, **kwargs) + +:func:`fac2` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.fac2(x) + +Binomial coefficients +.................................................... + +:func:`binomial` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.binomial(n,k) + + +Gamma function +.............. + +:func:`gamma` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.gamma(x, **kwargs) + +:func:`rgamma` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.rgamma(x, **kwargs) + +:func:`gammaprod` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.gammaprod(a, b) + +:func:`loggamma` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.loggamma(x) + + +Rising and falling factorials +............................. + +:func:`rf` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.rf(x,n) + +:func:`ff` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.ff(x,n) + +Beta function +............. + +:func:`beta` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.beta(x,y) + +:func:`betainc` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.betainc(a,b,x1=0,x2=1,regularized=False) + + +Super- and hyperfactorials +.......................... + +:func:`superfac` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.superfac(z) + +:func:`hyperfac` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.hyperfac(z) + +:func:`barnesg` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.barnesg(z) + + +Polygamma functions and harmonic numbers +........................................ + +:func:`psi`/:func:`digamma` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.psi(m, z) + +.. autofunction:: mpmath.digamma(z) + +:func:`harmonic` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.harmonic(z) diff --git a/latest/_sources/modules/mpmath/functions/hyperbolic.txt b/latest/_sources/modules/mpmath/functions/hyperbolic.txt new file mode 100644 index 000000000000..81243d30a060 --- /dev/null +++ b/latest/_sources/modules/mpmath/functions/hyperbolic.txt @@ -0,0 +1,56 @@ +Hyperbolic functions +-------------------- + +Hyperbolic functions +.................... + +:func:`cosh` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.cosh(x, **kwargs) + +:func:`sinh` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.sinh(x, **kwargs) + +:func:`tanh` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.tanh(x, **kwargs) + +:func:`sech` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.sech(x) + +:func:`csch` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.csch(x) + +:func:`coth` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.coth(x) + +Inverse hyperbolic functions +............................ + +:func:`acosh` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.acosh(x, **kwargs) + +:func:`asinh` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.asinh(x, **kwargs) + +:func:`atanh` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.atanh(x, **kwargs) + +:func:`asech` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.asech(x) + +:func:`acsch` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.acsch(x) + +:func:`acoth` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.acoth(x) diff --git a/latest/_sources/modules/mpmath/functions/hypergeometric.txt b/latest/_sources/modules/mpmath/functions/hypergeometric.txt new file mode 100644 index 000000000000..39c54b52e8ed --- /dev/null +++ b/latest/_sources/modules/mpmath/functions/hypergeometric.txt @@ -0,0 +1,115 @@ +Hypergeometric functions +------------------------ + +The functions listed in :doc:`expintegrals`, :doc:`bessel` and +:doc:`orthogonal`, and many other functions as well, are merely +particular instances of the generalized hypergeometric function `\,_pF_q`. +The functions listed in the following section enable efficient +direct evaluation of the underlying hypergeometric series, as +well as linear combinations, limits with respect to parameters, +and analytic continuations thereof. Extensions to twodimensional +series are also provided. See also the basic or q-analog of +the hypergeometric series in :doc:`qfunctions`. + +For convenience, most of the hypergeometric series of low order are +provided as standalone functions. They can equivalently be evaluated using +:func:`~mpmath.hyper`. As will be demonstrated in the respective docstrings, +all the ``hyp#f#`` functions implement analytic continuations and/or asymptotic +expansions with respect to the argument `z`, thereby permitting evaluation +for `z` anywhere in the complex plane. Functions of higher degree can be +computed via :func:`~mpmath.hyper`, but generally only in rapidly convergent +instances. + +Most hypergeometric and hypergeometric-derived functions accept optional +keyword arguments to specify options for :func:`hypercomb` or +:func:`hyper`. Some useful options are *maxprec*, *maxterms*, +*zeroprec*, *accurate_small*, *hmag*, *force_series*, +*asymp_tol* and *eliminate*. These options give control over what to +do in case of slow convergence, extreme loss of accuracy or +evaluation at zeros (these two cases cannot generally be +distinguished from each other automatically), +and singular parameter combinations. + +Common hypergeometric series +............................ + +:func:`hyp0f1` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.hyp0f1(a, z) + +:func:`hyp1f1` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.hyp1f1(a, b, z) + +:func:`hyp1f2` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.hyp1f2(a1, b1, b2, z) + +:func:`hyp2f0` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.hyp2f0(a, b, z) + +:func:`hyp2f1` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.hyp2f1(a, b, c, z) + +:func:`hyp2f2` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.hyp2f2(a1, a2, b1, b2, z) + +:func:`hyp2f3` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.hyp2f3(a1, a2, b1, b2, b3, z) + +:func:`hyp3f2` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.hyp3f2(a1, a2, a3, b1, b2, z) + +Generalized hypergeometric functions +.................................... + +:func:`hyper` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.hyper(a_s, b_s, z) + +:func:`hypercomb` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.hypercomb + +Meijer G-function +................................... + +:func:`meijerg` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.meijerg(a_s,b_s,z,r=1,**kwargs) + +Bilateral hypergeometric series +............................... + +:func:`bihyper` +^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.bihyper(a_s,b_s,z,**kwargs) + +Hypergeometric functions of two variables +............................................... + +:func:`hyper2d` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.hyper2d(a,b,x,y,**kwargs) + +:func:`appellf1` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.appellf1(a,b1,b2,c,x,y,**kwargs) + +:func:`appellf2` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.appellf2(a,b1,b2,c1,c2,x,y,**kwargs) + +:func:`appellf3` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.appellf3(a1,a2,b1,b2,c,x,y,**kwargs) + +:func:`appellf4` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.appellf4(a,b,c1,c2,x,y,**kwargs) + diff --git a/latest/_sources/modules/mpmath/functions/index.txt b/latest/_sources/modules/mpmath/functions/index.txt new file mode 100644 index 000000000000..404a95f57648 --- /dev/null +++ b/latest/_sources/modules/mpmath/functions/index.txt @@ -0,0 +1,21 @@ +Mathematical functions +====================== + +Mpmath implements the standard functions from Python's ``math`` and ``cmath`` modules, for both real and complex numbers and with arbitrary precision. Many other functions are also available in mpmath, including commonly-used variants of standard functions (such as the alternative trigonometric functions sec, csc, cot), but also a large number of "special functions" such as the gamma function, the Riemann zeta function, error functions, Bessel functions, etc. + +.. toctree:: + :maxdepth: 2 + + constants.rst + powers.rst + trigonometric.rst + hyperbolic.rst + gamma.rst + expintegrals.rst + bessel.rst + orthogonal.rst + hypergeometric.rst + elliptic.rst + zeta.rst + numtheory.rst + qfunctions.rst diff --git a/latest/_sources/modules/mpmath/functions/numtheory.txt b/latest/_sources/modules/mpmath/functions/numtheory.txt new file mode 100644 index 000000000000..1f45ae5d70cf --- /dev/null +++ b/latest/_sources/modules/mpmath/functions/numtheory.txt @@ -0,0 +1,93 @@ +Number-theoretical, combinatorial and integer functions +------------------------------------------------------- + +For factorial-type functions, including binomial coefficients, +double factorials, etc., see the separate +section :doc:`gamma`. + +Fibonacci numbers +................. + +:func:`fibonacci`/:func:`fib` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.fibonacci(n, **kwargs) + + +Bernoulli numbers and polynomials +................................. + +:func:`bernoulli` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.bernoulli(n) + +:func:`bernfrac` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.bernfrac(n) + +:func:`bernpoly` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.bernpoly(n,z) + +Euler numbers and polynomials +................................. + +:func:`eulernum` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.eulernum(n) + +:func:`eulerpoly` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.eulerpoly(n,z) + + +Bell numbers and polynomials +........................................... + +:func:`bell` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.bell(n,x) + + +Stirling numbers +........................................... + +:func:`stirling1` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.stirling1(n,k,exact=False) + +:func:`stirling2` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.stirling2(n,k,exact=False) + + + +Prime counting functions +........................ + +:func:`primepi` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.primepi(x) + +:func:`primepi2` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.primepi2(x) + +:func:`riemannr` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.riemannr(x) + + +Cyclotomic polynomials +...................... + +:func:`cyclotomic` +^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.cyclotomic(n,x) + + +Arithmetic functions +...................... + +:func:`mangoldt` +^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.mangoldt(n) diff --git a/latest/_sources/modules/mpmath/functions/orthogonal.txt b/latest/_sources/modules/mpmath/functions/orthogonal.txt new file mode 100644 index 000000000000..a9dc4bf545bb --- /dev/null +++ b/latest/_sources/modules/mpmath/functions/orthogonal.txt @@ -0,0 +1,79 @@ +Orthogonal polynomials +---------------------- + +An orthogonal polynomial sequence is a sequence of polynomials `P_0(x), P_1(x), \ldots` of degree `0, 1, \ldots`, which are mutually orthogonal in the sense that + +.. math :: + + \int_S P_n(x) P_m(x) w(x) dx = + \begin{cases} + c_n \ne 0 & \text{if $m = n$} \\ + 0 & \text{if $m \ne n$} + \end{cases} + +where `S` is some domain (e.g. an interval `[a,b] \in \mathbb{R}`) and `w(x)` is a fixed *weight function*. A sequence of orthogonal polynomials is determined completely by `w`, `S`, and a normalization convention (e.g. `c_n = 1`). Applications of orthogonal polynomials include function approximation and solution of differential equations. + +Orthogonal polynomials are sometimes defined using the differential equations they satisfy (as functions of `x`) or the recurrence relations they satisfy with respect to the order `n`. Other ways of defining orthogonal polynomials include differentiation formulas and generating functions. The standard orthogonal polynomials can also be represented as hypergeometric series (see :doc:`hypergeometric`), more specifically using the Gauss hypergeometric function `\,_2F_1` in most cases. The following functions are generally implemented using hypergeometric functions since this is computationally efficient and easily generalizes. + +For more information, see the `Wikipedia article on orthogonal polynomials `_. + +Legendre functions +....................................... + +:func:`legendre` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.legendre(n, x) + +:func:`legenp` +^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.legenp(n, m, z, type=2) + +:func:`legenq` +^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.legenq(n, m, z, type=2) + +Chebyshev polynomials +..................... + +:func:`chebyt` +^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.chebyt(n, x) + +:func:`chebyu` +^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.chebyu(n, x) + +Jacobi polynomials +.................. + +:func:`jacobi` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.jacobi(n, a, b, z) + +Gegenbauer polynomials +..................................... + +:func:`gegenbauer` +^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.gegenbauer(n, a, z) + +Hermite polynomials +..................................... + +:func:`hermite` +^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.hermite(n, z) + +Laguerre polynomials +....................................... + +:func:`laguerre` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.laguerre(n, a, z) + +Spherical harmonics +..................................... + +:func:`spherharm` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.spherharm(l, m, theta, phi) diff --git a/latest/_sources/modules/mpmath/functions/powers.txt b/latest/_sources/modules/mpmath/functions/powers.txt new file mode 100644 index 000000000000..a940b0b6f8e4 --- /dev/null +++ b/latest/_sources/modules/mpmath/functions/powers.txt @@ -0,0 +1,85 @@ +Powers and logarithms +--------------------- + +Nth roots +......... + +:func:`sqrt` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.sqrt(x, **kwargs) + +:func:`hypot` +^^^^^^^^^^^^^ +.. autofunction:: mpmath.hypot(x, y) + +:func:`cbrt` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.cbrt(x, **kwargs) + +:func:`root` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.root(z, n, k=0) + +:func:`unitroots` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.unitroots(n, primitive=False) + + +Exponentiation +.............. + +:func:`exp` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.exp(x, **kwargs) + +:func:`power` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.power(x, y) + +:func:`expj` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.expj(x, **kwargs) + +:func:`expjpi` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.expjpi(x, **kwargs) + +:func:`expm1` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.expm1(x) + +:func:`powm1` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.powm1(x, y) + + +Logarithms +.......... + +:func:`log` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.log(x, b=None) + +:func:`ln` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.ln(x, **kwargs) + +:func:`log10` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.log10(x) + + +Lambert W function +................................................... + +:func:`lambertw` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.lambertw(z, k=0) + + +Arithmetic-geometric mean +....................................... + +:func:`agm` +^^^^^^^^^^^^^^ +.. autofunction:: mpmath.agm(a, b=1) diff --git a/latest/_sources/modules/mpmath/functions/qfunctions.txt b/latest/_sources/modules/mpmath/functions/qfunctions.txt new file mode 100644 index 000000000000..1d04f9ef0301 --- /dev/null +++ b/latest/_sources/modules/mpmath/functions/qfunctions.txt @@ -0,0 +1,29 @@ +q-functions +------------------------------------------- + +q-Pochhammer symbol +.................................................. + +:func:`qp` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.qp(a, q=None, n=None, **kwargs) + + +q-gamma and factorial +.................................................. + +:func:`qgamma` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.qgamma(z, q, **kwargs) + +:func:`qfac` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.qfac(z, q, **kwargs) + +Hypergeometric q-series +.................................................. + +:func:`qhyper` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.qhyper(a_s, b_s, q, z, **kwargs) + diff --git a/latest/_sources/modules/mpmath/functions/trigonometric.txt b/latest/_sources/modules/mpmath/functions/trigonometric.txt new file mode 100644 index 000000000000..f66716abd3eb --- /dev/null +++ b/latest/_sources/modules/mpmath/functions/trigonometric.txt @@ -0,0 +1,117 @@ +Trigonometric functions +----------------------- + +Except where otherwise noted, the trigonometric functions +take a radian angle as input and the inverse trigonometric +functions return radian angles. + +The ordinary trigonometric functions are single-valued +functions defined everywhere in the complex plane +(except at the poles of tan, sec, csc, and cot). +They are defined generally via the exponential function, +e.g. + +.. math :: + + \cos(x) = \frac{e^{ix} + e^{-ix}}{2}. + +The inverse trigonometric functions are multivalued, +thus requiring branch cuts, and are generally real-valued +only on a part of the real line. Definitions and branch cuts +are given in the documentation of each function. +The branch cut conventions used by mpmath are essentially +the same as those found in most standard mathematical software, +such as Mathematica and Python's own ``cmath`` libary (as of Python 2.6; +earlier Python versions implement some functions +erroneously). + +Degree-radian conversion +........................................................... + +:func:`degrees` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.degrees(x) + +:func:`radians` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.radians(x) + +Trigonometric functions +....................... + +:func:`cos` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.cos(x, **kwargs) + +:func:`sin` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.sin(x, **kwargs) + +:func:`tan` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.tan(x, **kwargs) + +:func:`sec` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.sec(x) + +:func:`csc` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.csc(x) + +:func:`cot` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.cot(x) + +Trigonometric functions with modified argument +........................................................ + +:func:`cospi` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.cospi(x, **kwargs) + +:func:`sinpi` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.sinpi(x, **kwargs) + +Inverse trigonometric functions +................................................ + +:func:`acos` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.acos(x, **kwargs) + +:func:`asin` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.asin(x, **kwargs) + +:func:`atan` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.atan(x, **kwargs) + +:func:`atan2` +^^^^^^^^^^^^^ +.. autofunction:: mpmath.atan2(y, x) + +:func:`asec` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.asec(x) + +:func:`acsc` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.acsc(x) + +:func:`acot` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.acot(x) + +Sinc function +............. + +:func:`sinc` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.sinc(x) + +:func:`sincpi` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.sincpi(x) diff --git a/latest/_sources/modules/mpmath/functions/zeta.txt b/latest/_sources/modules/mpmath/functions/zeta.txt new file mode 100644 index 000000000000..c6a577a9d705 --- /dev/null +++ b/latest/_sources/modules/mpmath/functions/zeta.txt @@ -0,0 +1,104 @@ +Zeta functions, L-series and polylogarithms +------------------------------------------- + +This section includes the Riemann zeta functions +and associated functions pertaining to analytic number theory. + + +Riemann and Hurwitz zeta functions +.................................................. + +:func:`zeta` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.zeta(s,a=1,derivative=0) + + +Dirichlet L-series +.................................................. + +:func:`altzeta` +^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.altzeta(s) + +:func:`dirichlet` +^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.dirichlet(s,chi,derivative=0) + + +Stieltjes constants +................... + +:func:`stieltjes` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.stieltjes(n,a=1) + + +Zeta function zeros +...................................... + +These functions are used for the study of the Riemann zeta function +in the critical strip. + +:func:`zetazero` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.zetazero(n, verbose=False) + +:func:`nzeros` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.nzeros(t) + +:func:`siegelz` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.siegelz(t) + +:func:`siegeltheta` +^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.siegeltheta(t) + +:func:`grampoint` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.grampoint(n) + +:func:`backlunds` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.backlunds(t) + + +Lerch transcendent +................................ + +:func:`lerchphi` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.lerchphi(z,s,a) + + +Polylogarithms and Clausen functions +....................................... + +:func:`polylog` +^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.polylog(s,z) + +:func:`clsin` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.clsin(s, z) + +:func:`clcos` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.clcos(s, z) + +:func:`polyexp` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.polyexp(s,z) + + +Zeta function variants +.......................... + +:func:`primezeta` +^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.primezeta(s) + +:func:`secondzeta` +^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.secondzeta(s, a=0.015, **kwargs) diff --git a/latest/_sources/modules/mpmath/general.txt b/latest/_sources/modules/mpmath/general.txt new file mode 100644 index 000000000000..646e40bdf5e9 --- /dev/null +++ b/latest/_sources/modules/mpmath/general.txt @@ -0,0 +1,232 @@ +Utility functions +=============================================== + +This page lists functions that perform basic operations +on numbers or aid general programming. + +Conversion and printing +----------------------- + +:func:`mpmathify` / :func:`convert` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.mpmathify(x, strings=True) + +:func:`nstr` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.nstr(x, n=6, **kwargs) + +:func:`nprint` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.nprint(x, n=6, **kwargs) + +Arithmetic operations +--------------------- + +See also :func:`mpmath.sqrt`, :func:`mpmath.exp` etc., listed +in :doc:`functions/powers` + +:func:`fadd` +^^^^^^^^^^^^^ +.. autofunction:: mpmath.fadd + +:func:`fsub` +^^^^^^^^^^^^^ +.. autofunction:: mpmath.fsub + +:func:`fneg` +^^^^^^^^^^^^^ +.. autofunction:: mpmath.fneg + +:func:`fmul` +^^^^^^^^^^^^^ +.. autofunction:: mpmath.fmul + +:func:`fdiv` +^^^^^^^^^^^^^ +.. autofunction:: mpmath.fdiv + +:func:`fmod` +^^^^^^^^^^^^^ +.. autofunction:: mpmath.fmod(x, y) + +:func:`fsum` +^^^^^^^^^^^^^ +.. autofunction:: mpmath.fsum(terms, absolute=False, squared=False) + +:func:`fprod` +^^^^^^^^^^^^^ +.. autofunction:: mpmath.fprod(factors) + +:func:`fdot` +^^^^^^^^^^^^^ +.. autofunction:: mpmath.fdot(A, B=None, conjugate=False) + +Complex components +------------------ + +:func:`fabs` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.fabs(x) + +:func:`sign` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.sign(x) + +:func:`re` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.re(x) + +:func:`im` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.im(x) + +:func:`arg` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.arg(x) + +:func:`conj` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.conj(x) + +:func:`polar` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.polar(x) + +:func:`rect` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.rect(x) + +Integer and fractional parts +----------------------------- + +:func:`floor` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.floor(x) + +:func:`ceil` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.ceil(x) + +:func:`nint` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.nint(x) + +:func:`frac` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.frac(x) + +Tolerances and approximate comparisons +-------------------------------------- + +:func:`chop` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.chop(x, tol=None) + +:func:`almosteq` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.almosteq(s, t, rel_eps=None, abs_eps=None) + +Properties of numbers +------------------------------------- + +:func:`isinf` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.isinf(x) + +:func:`isnan` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.isnan(x) + +:func:`isnormal` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.isnormal(x) + +:func:`isfinite` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.isfinite(x) + +:func:`isint` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.isint(x, gaussian=False) + +:func:`ldexp` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.ldexp(x, n) + +:func:`frexp` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.frexp(x, n) + +:func:`mag` +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.mag(x) + +:func:`nint_distance` +^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.nint_distance(x) + +.. :func:`absmin` +.. ^^^^^^^^^^^^^^^^^^^^ +.. .. autofunction:: mpmath.absmin(x) +.. .. autofunction:: mpmath.absmax(x) + +Number generation +----------------- + +:func:`fraction` +^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.fraction(p,q) + +:func:`rand` +^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.rand() + +:func:`arange` +^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.arange(*args) + +:func:`linspace` +^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.linspace(*args, **kwargs) + +Precision management +-------------------- + +:func:`autoprec` +^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.autoprec + +:func:`workprec` +^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.workprec + +:func:`workdps` +^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.workdps + +:func:`extraprec` +^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.extraprec + +:func:`extradps` +^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.extradps + +Performance and debugging +------------------------------------ + +:func:`memoize` +^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.memoize + +:func:`maxcalls` +^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.maxcalls + +:func:`monitor` +^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.monitor + +:func:`timing` +^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.timing diff --git a/latest/_sources/modules/mpmath/identification.txt b/latest/_sources/modules/mpmath/identification.txt new file mode 100644 index 000000000000..74c36f0e8b6c --- /dev/null +++ b/latest/_sources/modules/mpmath/identification.txt @@ -0,0 +1,31 @@ +Number identification +===================== + +Most function in mpmath are concerned with producing approximations from exact mathematical formulas. It is also useful to consider the inverse problem: given only a decimal approximation for a number, such as 0.7320508075688772935274463, is it possible to find an exact formula? + +Subject to certain restrictions, such "reverse engineering" is indeed possible thanks to the existence of *integer relation algorithms*. Mpmath implements the PSLQ algorithm (developed by H. Ferguson), which is one such algorithm. + +Automated number recognition based on PSLQ is not a silver bullet. Any occurring transcendental constants (`\pi`, `e`, etc) must be guessed by the user, and the relation between those constants in the formula must be linear (such as `x = 3 \pi + 4 e`). More complex formulas can be found by combining PSLQ with functional transformations; however, this is only feasible to a limited extent since the computation time grows exponentially with the number of operations that need to be combined. + +The number identification facilities in mpmath are inspired by the `Inverse Symbolic Calculator `_ (ISC). The ISC is more powerful than mpmath, as it uses a lookup table of millions of precomputed constants (thereby mitigating the problem with exponential complexity). + +Constant recognition +----------------------------------- + +:func:`identify` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.identify + +Algebraic identification +--------------------------------------- + +:func:`findpoly` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.findpoly + +Integer relations (PSLQ) +---------------------------- + +:func:`pslq` +^^^^^^^^^^^^^^^^ +.. autofunction:: mpmath.pslq diff --git a/latest/_sources/modules/mpmath/index.txt b/latest/_sources/modules/mpmath/index.txt new file mode 100644 index 000000000000..334ba025e2c8 --- /dev/null +++ b/latest/_sources/modules/mpmath/index.txt @@ -0,0 +1,53 @@ +.. mpmath documentation master file, created by sphinx-quickstart on Fri Mar 28 13:50:14 2008. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to mpmath's documentation! +================================== + +Mpmath is a Python library for arbitrary-precision floating-point arithmetic. +For general information about mpmath, see the project website http://code.google.com/p/mpmath/ + +These documentation pages include general information as well as docstring listing with extensive use of examples that can be run in the interactive Python interpreter. For quick access to the docstrings of individual functions, use the `index listing `_, or type ``help(mpmath.function_name)`` in the Python interactive prompt. + +Introduction +------------ + +.. toctree :: + :maxdepth: 2 + + setup.rst + basics.rst + +Basic features +---------------- + +.. toctree :: + :maxdepth: 2 + + contexts.rst + general.rst + plotting.rst + +Advanced mathematics +-------------------- + +On top of its support for arbitrary-precision arithmetic, mpmath +provides extensive support for transcendental functions, evaluation of sums, integrals, limits, roots, and so on. + +.. toctree :: + :maxdepth: 2 + + functions/index.rst + calculus/index.rst + matrices.rst + identification.rst + +End matter +---------- + +.. toctree :: + :maxdepth: 2 + + technical.rst + references.rst diff --git a/latest/_sources/modules/mpmath/matrices.txt b/latest/_sources/modules/mpmath/matrices.txt new file mode 100644 index 000000000000..ebc250947b30 --- /dev/null +++ b/latest/_sources/modules/mpmath/matrices.txt @@ -0,0 +1,541 @@ +Matrices +======== + +Creating matrices +----------------- + +Basic methods +............. + +Matrices in mpmath are implemented using dictionaries. Only non-zero values are +stored, so it is cheap to represent sparse matrices. + +The most basic way to create one is to use the ``matrix`` class directly. You +can create an empty matrix specifying the dimensions:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> matrix(2) + matrix( + [['0.0', '0.0'], + ['0.0', '0.0']]) + >>> matrix(2, 3) + matrix( + [['0.0', '0.0', '0.0'], + ['0.0', '0.0', '0.0']]) + +Calling ``matrix`` with one dimension will create a square matrix. + +To access the dimensions of a matrix, use the ``rows`` or ``cols`` keyword:: + + >>> A = matrix(3, 2) + >>> A + matrix( + [['0.0', '0.0'], + ['0.0', '0.0'], + ['0.0', '0.0']]) + >>> A.rows + 3 + >>> A.cols + 2 + +You can also change the dimension of an existing matrix. This will set the +new elements to 0. If the new dimension is smaller than before, the +concerning elements are discarded:: + + >>> A.rows = 2 + >>> A + matrix( + [['0.0', '0.0'], + ['0.0', '0.0']]) + +Internally ``convert`` is applied every time an element is set. This is +done using the syntax A[row,column], counting from 0:: + + >>> A = matrix(2) + >>> A[1,1] = 1 + 1j + >>> print A + [0.0 0.0] + [0.0 (1.0 + 1.0j)] + +A more comfortable way to create a matrix lets you use nested lists:: + + >>> matrix([[1, 2], [3, 4]]) + matrix( + [['1.0', '2.0'], + ['3.0', '4.0']]) + +Advanced methods +................ + +Convenient functions are available for creating various standard matrices:: + + >>> zeros(2) + matrix( + [['0.0', '0.0'], + ['0.0', '0.0']]) + >>> ones(2) + matrix( + [['1.0', '1.0'], + ['1.0', '1.0']]) + >>> diag([1, 2, 3]) # diagonal matrix + matrix( + [['1.0', '0.0', '0.0'], + ['0.0', '2.0', '0.0'], + ['0.0', '0.0', '3.0']]) + >>> eye(2) # identity matrix + matrix( + [['1.0', '0.0'], + ['0.0', '1.0']]) + +You can even create random matrices:: + + >>> randmatrix(2) # doctest:+SKIP + matrix( + [['0.53491598236191806', '0.57195669543302752'], + ['0.85589992269513615', '0.82444367501382143']]) + +Vectors +....... + +Vectors may also be represented by the ``matrix`` class (with rows = 1 or cols = 1). +For vectors there are some things which make life easier. A column vector can +be created using a flat list, a row vectors using an almost flat nested list:: + + >>> matrix([1, 2, 3]) + matrix( + [['1.0'], + ['2.0'], + ['3.0']]) + >>> matrix([[1, 2, 3]]) + matrix( + [['1.0', '2.0', '3.0']]) + +Optionally vectors can be accessed like lists, using only a single index:: + + >>> x = matrix([1, 2, 3]) + >>> x[1] + mpf('2.0') + >>> x[1,0] + mpf('2.0') + +Other +..... + +Like you probably expected, matrices can be printed:: + + >>> print randmatrix(3) # doctest:+SKIP + [ 0.782963853573023 0.802057689719883 0.427895717335467] + [0.0541876859348597 0.708243266653103 0.615134039977379] + [ 0.856151514955773 0.544759264818486 0.686210904770947] + +Use ``nstr`` or ``nprint`` to specify the number of digits to print:: + + >>> nprint(randmatrix(5), 3) # doctest:+SKIP + [2.07e-1 1.66e-1 5.06e-1 1.89e-1 8.29e-1] + [6.62e-1 6.55e-1 4.47e-1 4.82e-1 2.06e-2] + [4.33e-1 7.75e-1 6.93e-2 2.86e-1 5.71e-1] + [1.01e-1 2.53e-1 6.13e-1 3.32e-1 2.59e-1] + [1.56e-1 7.27e-2 6.05e-1 6.67e-2 2.79e-1] + +As matrices are mutable, you will need to copy them sometimes:: + + >>> A = matrix(2) + >>> A + matrix( + [['0.0', '0.0'], + ['0.0', '0.0']]) + >>> B = A.copy() + >>> B[0,0] = 1 + >>> B + matrix( + [['1.0', '0.0'], + ['0.0', '0.0']]) + >>> A + matrix( + [['0.0', '0.0'], + ['0.0', '0.0']]) + +Finally, it is possible to convert a matrix to a nested list. This is very useful, +as most Python libraries involving matrices or arrays (namely NumPy or SymPy) +support this format:: + + >>> B.tolist() + [[mpf('1.0'), mpf('0.0')], [mpf('0.0'), mpf('0.0')]] + + +Matrix operations +----------------- + +You can add and substract matrices of compatible dimensions:: + + >>> A = matrix([[1, 2], [3, 4]]) + >>> B = matrix([[-2, 4], [5, 9]]) + >>> A + B + matrix( + [['-1.0', '6.0'], + ['8.0', '13.0']]) + >>> A - B + matrix( + [['3.0', '-2.0'], + ['-2.0', '-5.0']]) + >>> A + ones(3) # doctest:+ELLIPSIS + Traceback (most recent call last): + File "", line 1, in + File "...", line 238, in __add__ + raise ValueError('incompatible dimensions for addition') + ValueError: incompatible dimensions for addition + +It is possible to multiply or add matrices and scalars. In the latter case the +operation will be done element-wise:: + + >>> A * 2 + matrix( + [['2.0', '4.0'], + ['6.0', '8.0']]) + >>> A / 4 + matrix( + [['0.25', '0.5'], + ['0.75', '1.0']]) + >>> A - 1 + matrix( + [['0.0', '1.0'], + ['2.0', '3.0']]) + +Of course you can perform matrix multiplication, if the dimensions are +compatible:: + + >>> A * B + matrix( + [['8.0', '22.0'], + ['14.0', '48.0']]) + >>> matrix([[1, 2, 3]]) * matrix([[-6], [7], [-2]]) + matrix( + [['2.0']]) + +You can raise powers of square matrices:: + + >>> A**2 + matrix( + [['7.0', '10.0'], + ['15.0', '22.0']]) + +Negative powers will calculate the inverse:: + + >>> A**-1 + matrix( + [['-2.0', '1.0'], + ['1.5', '-0.5']]) + >>> nprint(A * A**-1, 3) + [ 1.0 1.08e-19] + [-2.17e-19 1.0] + +Matrix transposition is straightforward:: + + >>> A = ones(2, 3) + >>> A + matrix( + [['1.0', '1.0', '1.0'], + ['1.0', '1.0', '1.0']]) + >>> A.T + matrix( + [['1.0', '1.0'], + ['1.0', '1.0'], + ['1.0', '1.0']]) + + +Norms +..... + +Sometimes you need to know how "large" a matrix or vector is. Due to their +multidimensional nature it's not possible to compare them, but there are +several functions to map a matrix or a vector to a positive real number, the +so called norms. + +.. autofunction :: mpmath.norm + +.. autofunction :: mpmath.mnorm + + +Linear algebra +-------------- + +Decompositions +.............. + +.. autofunction :: mpmath.cholesky + + +Linear equations +................ + +Basic linear algebra is implemented; you can for example solve the linear +equation system:: + + x + 2*y = -10 + 3*x + 4*y = 10 + +using ``lu_solve``:: + + >>> A = matrix([[1, 2], [3, 4]]) + >>> b = matrix([-10, 10]) + >>> x = lu_solve(A, b) + >>> x + matrix( + [['30.0'], + ['-20.0']]) + +If you don't trust the result, use ``residual`` to calculate the residual ||A*x-b||:: + + >>> residual(A, x, b) + matrix( + [['3.46944695195361e-18'], + ['3.46944695195361e-18']]) + >>> str(eps) + '2.22044604925031e-16' + +As you can see, the solution is quite accurate. The error is caused by the +inaccuracy of the internal floating point arithmetic. Though, it's even smaller +than the current machine epsilon, which basically means you can trust the +result. + +If you need more speed, use NumPy, or use ``fp`` instead ``mp`` matrices +and methods:: + + >>> A = fp.matrix([[1, 2], [3, 4]]) + >>> b = fp.matrix([-10, 10]) + >>> fp.lu_solve(A, b) + matrix( + [['30.0'], + ['-20.0']]) + +``lu_solve`` accepts overdetermined systems. It is usually not possible to solve +such systems, so the residual is minimized instead. Internally this is done +using Cholesky decomposition to compute a least squares approximation. This means +that that ``lu_solve`` will square the errors. If you can't afford this, use +``qr_solve`` instead. It is twice as slow but more accurate, and it calculates +the residual automatically. + + +Matrix factorization +.................... + +The function ``lu`` computes an explicit LU factorization of a matrix:: + + >>> P, L, U = lu(matrix([[0,2,3],[4,5,6],[7,8,9]])) + >>> print P + [0.0 0.0 1.0] + [1.0 0.0 0.0] + [0.0 1.0 0.0] + >>> print L + [ 1.0 0.0 0.0] + [ 0.0 1.0 0.0] + [0.571428571428571 0.214285714285714 1.0] + >>> print U + [7.0 8.0 9.0] + [0.0 2.0 3.0] + [0.0 0.0 0.214285714285714] + >>> print P.T*L*U + [0.0 2.0 3.0] + [4.0 5.0 6.0] + [7.0 8.0 9.0] + +The function ``qr`` computes a QR factorization of a matrix:: + + >>> A = matrix([[1, 2], [3, 4], [1, 1]]) + >>> Q, R = qr(A) + >>> print Q + [-0.301511344577764 0.861640436855329 0.408248290463863] + [-0.904534033733291 -0.123091490979333 -0.408248290463863] + [-0.301511344577764 -0.492365963917331 0.816496580927726] + >>> print R + [-3.3166247903554 -4.52267016866645] + [ 0.0 0.738548945875996] + [ 0.0 0.0] + >>> print Q * R + [1.0 2.0] + [3.0 4.0] + [1.0 1.0] + >>> print chop(Q.T * Q) + [1.0 0.0 0.0] + [0.0 1.0 0.0] + [0.0 0.0 1.0] + + +The singular value decomposition +................................ + +The routines ``svd_r`` and ``svd_c`` compute the singular value decomposition +of a real or complex matrix A. ``svd`` is an unified interface calling +either ``svd_r`` or ``svd_c`` depending on whether *A* is real or complex. + +Given *A*, two orthogonal (*A* real) or unitary (*A* complex) matrices *U* and *V* +are calculated such that + +.. math :: + + A = U S V, \quad U' U = 1, \quad V V' = 1 + +where *S* is a suitable shaped matrix whose off-diagonal elements are zero. +Here ' denotes the hermitian transpose (i.e. transposition and complex +conjugation). The diagonal elements of *S* are the singular values of *A*, +i.e. the square roots of the eigenvalues of `A' A` or `A A'`. + +Examples:: + + >>> from mpmath import mp + >>> A = mp.matrix([[2, -2, -1], [3, 4, -2], [-2, -2, 0]]) + >>> S = mp.svd_r(A, compute_uv = False) + >>> print S + [6.0] + [3.0] + [1.0] + >>> U, S, V = mp.svd_r(A) + >>> print mp.chop(A - U * mp.diag(S) * V) + [0.0 0.0 0.0] + [0.0 0.0 0.0] + [0.0 0.0 0.0] + + +The Schur decomposition +....................... + +This routine computes the Schur decomposition of a square matrix *A*. +Given *A*, a unitary matrix *Q* is determined such that + +.. math :: + + Q' A Q = R, \quad Q' Q = Q Q' = 1 + +where *R* is an upper right triangular matrix. Here ' denotes the +hermitian transpose (i.e. transposition and conjugation). + +Examples:: + + >>> from mpmath import mp + >>> A = mp.matrix([[3, -1, 2], [2, 5, -5], [-2, -3, 7]]) + >>> Q, R = mp.schur(A) + >>> mp.nprint(R, 3) # doctest:+SKIP + [2.0 0.417 -2.53] + [0.0 4.0 -4.74] + [0.0 0.0 9.0] + >>> print(mp.chop(A - Q * R * Q.transpose_conj())) + [0.0 0.0 0.0] + [0.0 0.0 0.0] + [0.0 0.0 0.0] + + +The eigenvalue problem +...................... + +The routine ``eig`` solves the (ordinary) eigenvalue problem for a real or complex +square matrix *A*. Given *A*, a vector *E* and matrices *ER* and *EL* are calculated such that + +.. code :: + + A ER[:,i] = E[i] ER[:,i] + EL[i,:] A = EL[i,:] E[i] + +*E* contains the eigenvalues of *A*. The columns of *ER* contain the right eigenvectors +of *A* whereas the rows of *EL* contain the left eigenvectors. + + +Examples:: + + >>> from mpmath import mp + >>> A = mp.matrix([[3, -1, 2], [2, 5, -5], [-2, -3, 7]]) + >>> E, ER = mp.eig(A) + >>> print(mp.chop(A * ER[:,0] - E[0] * ER[:,0])) + [0.0] + [0.0] + [0.0] + >>> E, EL, ER = mp.eig(A,left = True, right = True) + >>> E, EL, ER = mp.eig_sort(E, EL, ER) + >>> mp.nprint(E) + [2.0, 4.0, 9.0] + >>> print(mp.chop(A * ER[:,0] - E[0] * ER[:,0])) + [0.0] + [0.0] + [0.0] + >>> print(mp.chop( EL[0,:] * A - EL[0,:] * E[0])) + [0.0 0.0 0.0] + + +The symmetric eigenvalue problem +................................ + +The routines ``eigsy`` and ``eighe`` solve the (ordinary) eigenvalue problem +for a real symmetric or complex hermitian square matrix *A*. +``eigh`` is an unified interface for this two functions calling either +``eigsy`` or ``eighe`` depending on whether *A* is real or complex. + +Given *A*, an orthogonal (*A* real) or unitary matrix *Q* (*A* complex) is +calculated which diagonalizes A: + +.. math :: + + Q' A Q = \operatorname{diag}(E), \quad Q Q' = Q' Q = 1 + +Here diag(*E*) a is diagonal matrix whose diagonal is *E*. +' denotes the hermitian transpose (i.e. ordinary transposition and +complex conjugation). + +The columns of *Q* are the eigenvectors of *A* and *E* contains the eigenvalues: + +.. code :: + + A Q[:,i] = E[i] Q[:,i] + +Examples:: + + >>> from mpmath import mp + >>> A = mp.matrix([[3, 2], [2, 0]]) + >>> E = mp.eigsy(A, eigvals_only = True) + >>> print E + [-1.0] + [ 4.0] + >>> A = mp.matrix([[1, 2], [2, 3]]) + >>> E, Q = mp.eigsy(A) # alternative: E, Q = mp.eigh(A) + >>> print mp.chop(A * Q[:,0] - E[0] * Q[:,0]) + [0.0] + [0.0] + >>> A = mp.matrix([[1, 2 + 5j], [2 - 5j, 3]]) + >>> E, Q = mp.eighe(A) # alternative: E, Q = mp.eigh(A) + >>> print mp.chop(A * Q[:,0] - E[0] * Q[:,0]) + [0.0] + [0.0] + + +Interval and double-precision matrices +-------------------------------------- + +The ``iv.matrix`` and ``fp.matrix`` classes convert inputs +to intervals and Python floating-point numbers respectively. + +Interval matrices can be used to perform linear algebra operations +with rigorous error tracking:: + + >>> a = iv.matrix([['0.1','0.3','1.0'], + ... ['7.1','5.5','4.8'], + ... ['3.2','4.4','5.6']]) + >>> + >>> b = iv.matrix(['4','0.6','0.5']) + >>> c = iv.lu_solve(a, b) + >>> print c + [ [5.2582327113062393041, 5.2582327113062749951]] + [[-13.155049396267856583, -13.155049396267821167]] + [ [7.4206915477497212555, 7.4206915477497310922]] + >>> print a*c + [ [3.9999999999999866773, 4.0000000000000133227]] + [[0.59999999999972430942, 0.60000000000027142733]] + [[0.49999999999982236432, 0.50000000000018474111]] + +Matrix functions +---------------- + +.. autofunction :: mpmath.expm +.. autofunction :: mpmath.cosm +.. autofunction :: mpmath.sinm +.. autofunction :: mpmath.sqrtm +.. autofunction :: mpmath.logm +.. autofunction :: mpmath.powm diff --git a/latest/_sources/modules/mpmath/plotting.txt b/latest/_sources/modules/mpmath/plotting.txt new file mode 100644 index 000000000000..a7821c679282 --- /dev/null +++ b/latest/_sources/modules/mpmath/plotting.txt @@ -0,0 +1,32 @@ +Plotting +======== + +If `matplotlib `_ is available, the functions ``plot`` and ``cplot`` in mpmath can be used to plot functions respectively as x-y graphs and in the complex plane. Also, ``splot`` can be used to produce 3D surface plots. + +Function curve plots +----------------------- + +.. image:: plot.png + +Output of ``plot([cos, sin], [-4, 4])`` + +.. autofunction:: mpmath.plot + +Complex function plots +------------------------- + +.. image:: cplot.png + +Output of ``fp.cplot(fp.gamma, points=100000)`` + +.. autofunction:: mpmath.cplot + +3D surface plots +---------------- + +.. image:: splot.png + +Output of ``splot`` for the donut example. + +.. autofunction:: mpmath.splot + diff --git a/latest/_sources/modules/mpmath/references.txt b/latest/_sources/modules/mpmath/references.txt new file mode 100644 index 000000000000..379728d24a6c --- /dev/null +++ b/latest/_sources/modules/mpmath/references.txt @@ -0,0 +1,50 @@ +References +=================== + +The following is a non-comprehensive list of works used in the development of mpmath +or cited for examples or mathematical definitions used in this documentation. +References not listed here can be found in the source code. + +.. [AbramowitzStegun] M Abramowitz & I Stegun. *Handbook of Mathematical Functions, 9th Ed.*, Tenth Printing, December 1972, with corrections (electronic copy: http://people.math.sfu.ca/~cbm/aands/) + +.. [Bailey] D H Bailey. "Tanh-Sinh High-Precision Quadrature", http://crd-legacy.lbl.gov/~dhbailey/dhbpapers/dhb-tanh-sinh.pdf + +.. [BenderOrszag] C M Bender & S A Orszag. *Advanced Mathematical Methods for + Scientists and Engineers*, Springer 1999 + +.. [BorweinBailey] J Borwein, D H Bailey & R Girgensohn. *Experimentation in Mathematics - Computational Paths to Discovery*, A K Peters, 2003 + +.. [BorweinBorwein] J Borwein & P B Borwein. *Pi and the AGM: A Study in Analytic Number Theory and Computational Complexity*, Wiley 1987 + +.. [BorweinZeta] P Borwein. "An Efficient Algorithm for the Riemann Zeta Function", http://www.cecm.sfu.ca/personal/pborwein/PAPERS/P155.pdf + +.. [CabralRosetti] L G Cabral-Rosetti & M A Sanchis-Lozano. "Appell Functions and the Scalar One-Loop Three-point Integrals in Feynman Diagrams". http://arxiv.org/abs/hep-ph/0206081 + +.. [Carlson] B C Carlson. "Numerical computation of real or complex elliptic integrals". http://arxiv.org/abs/math/9409227v1 + +.. [Corless] R M Corless et al. "On the Lambert W function", Adv. Comp. Math. 5 (1996) 329-359. http://www.apmaths.uwo.ca/~djeffrey/Offprints/W-adv-cm.pdf + +.. [DLMF] NIST Digital Library of Mathematical Functions. http://dlmf.nist.gov/ + +.. [GradshteynRyzhik] I S Gradshteyn & I M Ryzhik, A Jeffrey & D Zwillinger (eds.), *Table of Integrals, Series and Products*, Seventh edition (2007), Elsevier + +.. [GravesMorris] P R Graves-Morris, D E Roberts & A Salam. "The epsilon algorithm and related topics", *Journal of Computational and Applied Mathematics*, Volume 122, Issue 1-2 (October 2000) + +.. [MPFR] The MPFR team. "The MPFR Library: Algorithms and Proofs", http://www.mpfr.org/algorithms.pdf + +.. [Slater] L J Slater. *Generalized Hypergeometric Functions*. Cambridge University Press, 1966 + +.. [Spouge] J L Spouge. "Computation of the gamma, digamma, and trigamma functions", SIAM J. Numer. Anal. Vol. 31, No. 3, pp. 931-944, June 1994. + +.. [SrivastavaKarlsson] H M Srivastava & P W Karlsson. *Multiple Gaussian Hypergeometric Series*. Ellis Horwood, 1985. + +.. [Vidunas] R Vidunas. "Identities between Appell's and hypergeometric functions". http://arxiv.org/abs/0804.0655 + +.. [Weisstein] E W Weisstein. *MathWorld*. http://mathworld.wolfram.com/ + +.. [WhittakerWatson] E T Whittaker & G N Watson. *A Course of Modern Analysis*. 4th Ed. 1946 + Cambridge University Press + +.. [Wikipedia] *Wikipedia, the free encyclopedia*. http://en.wikipedia.org/wiki/Main_Page + +.. [WolframFunctions] Wolfram Research, Inc. *The Wolfram Functions Site*. http://functions.wolfram.com/ diff --git a/latest/_sources/modules/mpmath/setup.txt b/latest/_sources/modules/mpmath/setup.txt new file mode 100644 index 000000000000..b6bfbc6a1120 --- /dev/null +++ b/latest/_sources/modules/mpmath/setup.txt @@ -0,0 +1,179 @@ +Setting up mpmath +================= + +Download and installation +------------------------- + +Installer +......... + +The mpmath setup files can be downloaded from the `mpmath download page `_ or the `Python Package Index `_. Download the source package (available as both .zip and .tar.gz), extract it, open the extracted directory, and run + + ``python setup.py install`` + +If you are using Windows, you can download the binary installer + + ``mpmath-(version).win32.exe`` + +from the mpmath website or the Python Package Index. Run the installer and follow the instructions. + +Using setuptools +................ + +If you have `setuptools `_ installed, you can download and install mpmath in one step by running: + + ``easy_install mpmath`` + +or + + ``python -m easy_install mpmath`` + +If you have an old version of mpmath installed already, you may have to pass ``easy_install`` the ``-U`` flag to force an upgrade. + + +Debian/Ubuntu +............. + +Debian and Ubuntu users can install mpmath with + + ``sudo apt-get install python-mpmath`` + +See `debian `_ and `ubuntu `_ package information; please verify that you are getting the latest version. + +OpenSUSE +........ + +Mpmath is provided in the "Science" repository for all recent versions of `openSUSE `_. To add this repository to the YAST software management tool, see http://en.opensuse.org/SDB:Add_package_repositories + +Look up http://download.opensuse.org/repositories/science/ for a list +of supported OpenSUSE versions and use http://download.opensuse.org/repositories/science/openSUSE_11.1/ +(or accordingly for your OpenSUSE version) as the repository URL for YAST. + +Current development version +........................... + +See http://code.google.com/p/mpmath/source/checkout for instructions on how to check out the mpmath Subversion repository. The source code can also be browsed online from the Google Code page. + +Checking that it works +...................... + +After the setup has completed, you should be able to fire up the interactive Python interpreter and do the following:: + + >>> from mpmath import * + >>> mp.dps = 50 + >>> print mpf(2) ** mpf('0.5') + 1.4142135623730950488016887242096980785696718753769 + >>> print 2*pi + 6.2831853071795864769252867665590057683943387987502 + +*Note: if you have are upgrading mpmath from an earlier version, you may have to manually uninstall the old version or remove the old files.* + +Using gmpy (optional) +--------------------- + +By default, mpmath uses Python integers internally. If `gmpy `_ version 1.03 or later is installed on your system, mpmath will automatically detect it and transparently use gmpy integers intead. This makes mpmath much faster, especially at high precision (approximately above 100 digits). + +To verify that mpmath uses gmpy, check the internal variable ``BACKEND`` is not equal to 'python': + + >>> import mpmath.libmp + >>> mpmath.libmp.BACKEND # doctest:+SKIP + 'gmpy' + +The gmpy mode can be disabled by setting the MPMATH_NOGMPY environment variable. Note that the mode cannot be switched during runtime; mpmath must be re-imported for this change to take effect. + +Running tests +------------- + +It is recommended that you run mpmath's full set of unit tests to make sure everything works. The tests are located in the ``tests`` subdirectory of the main mpmath directory. They can be run in the interactive interpreter using the ``runtests()`` function:: + + import mpmath + mpmath.runtests() + +Alternatively, they can be run from the ``tests`` directory via + + ``python runtests.py`` + +The tests should finish in about a minute. If you have `psyco `_ installed, the tests can also be run with + + ``python runtests.py -psyco`` + +which will cut the running time in half. + +If any test fails, please send a detailed bug report to the `mpmath issue tracker `_. The tests can also be run with `py.test `_. This will sometimes generate more useful information in case of a failure. + +To run the tests with support for gmpy disabled, use + + ``python runtests.py -nogmpy`` + +To enable extra diagnostics, use + + ``python runtests.py -strict`` + +Compiling the documentation +--------------------------- + +If you downloaded the source package, the text source for these documentation pages is included in the ``doc`` directory. The documentation can be compiled to pretty HTML using `Sphinx `_. Go to the ``doc`` directory and run + + ``python build.py`` + +You can also test that all the interactive examples in the documentation work by running + + ``python run_doctest.py`` + +and by running the individual ``.py`` files in the mpmath source. + +(The doctests may take several minutes.) + +Finally, some additional demo scripts are available in the ``demo`` directory included in the source package. + +Mpmath under SymPy +-------------------- + +Mpmath is available as a subpackage of `SymPy `_. With SymPy installed, you can just do + + ``import sympy.mpmath as mpmath`` + +instead of ``import mpmath``. Note that the SymPy version of mpmath might not be the most recent. You can make a separate mpmath installation even if SymPy is installed; the two mpmath packages will not interfere with each other. + +Mpmath under Sage +------------------- + +Mpmath is a standard package in `Sage `_, in version 4.1 or later of Sage. +Mpmath is preinstalled a regular Python module, and can be imported as usual within Sage:: + + ---------------------------------------------------------------------- + | Sage Version 4.1, Release Date: 2009-07-09 | + | Type notebook() for the GUI, and license() for information. | + ---------------------------------------------------------------------- + sage: import mpmath + sage: mpmath.mp.dps = 50 + sage: print mpmath.mpf(2) ** 0.5 + 1.4142135623730950488016887242096980785696718753769 + +The mpmath installation under Sage automatically use Sage integers for asymptotically fast arithmetic, +so there is no need to install GMPY:: + + sage: mpmath.libmp.BACKEND + 'sage' + +In Sage, mpmath can alternatively be imported via the interface library +``sage.libs.mpmath.all``. For example:: + + sage: import sage.libs.mpmath.all as mpmath + +This module provides a few extra conversion functions, including :func:`call` +which permits calling any mpmath function with Sage numbers as input, and getting +Sage ``RealNumber`` or ``ComplexNumber`` instances +with the appropriate precision back:: + + sage: w = mpmath.call(mpmath.erf, 2+3*I, prec=100) + sage: w + -20.829461427614568389103088452 + 8.6873182714701631444280787545*I + sage: type(w) + + sage: w.prec() + 100 + +See the help for ``sage.libs.mpmath.all`` for further information. + + diff --git a/latest/_sources/modules/mpmath/technical.txt b/latest/_sources/modules/mpmath/technical.txt new file mode 100644 index 000000000000..4adf617f921a --- /dev/null +++ b/latest/_sources/modules/mpmath/technical.txt @@ -0,0 +1,159 @@ +Precision and representation issues +=================================== + +Most of the time, using mpmath is simply a matter of setting the desired precision and entering a formula. For verification purposes, a quite (but not always!) reliable technique is to calculate the same thing a second time at a higher precision and verifying that the results agree. + +To perform more advanced calculations, it is important to have some understanding of how mpmath works internally and what the possible sources of error are. This section gives an overview of arbitrary-precision binary floating-point arithmetic and some concepts from numerical analysis. + +The following concepts are important to understand: + +* The main sources of numerical errors are rounding and cancellation, which are due to the use of finite-precision arithmetic, and truncation or approximation errors, which are due to approximating infinite sequences or continuous functions by a finite number of samples. +* Errors propagate between calculations. A small error in the input may result in a large error in the output. +* Most numerical algorithms for complex problems (e.g. integrals, derivatives) give wrong answers for sufficiently ill-behaved input. Sometimes virtually the only way to get a wrong answer is to design some very contrived input, but at other times the chance of accidentally obtaining a wrong result even for reasonable-looking input is quite high. +* Like any complex numerical software, mpmath has implementation bugs. You should be reasonably suspicious about any results computed by mpmath, even those it claims to be able to compute correctly! If possible, verify results analytically, try different algorithms, and cross-compare with other software. + +Precision, error and tolerance +------------------------------ + +The following terms are common in this documentation: + +- *Precision* (or *working precision*) is the precision at which floating-point arithmetic operations are performed. +- *Error* is the difference between a computed approximation and the exact result. +- *Accuracy* is the inverse of error. +- *Tolerance* is the maximum error (or minimum accuracy) desired in a result. + +Error and accuracy can be measured either directly, or logarithmically in bits or digits. Specifically, if a `\hat y` is an approximation for `y`, then + +- (Direct) absolute error = `|\hat y - y|` +- (Direct) relative error = `|\hat y - y| |y|^{-1}` +- (Direct) absolute accuracy = `|\hat y - y|^{-1}` +- (Direct) relative accuracy = `|\hat y - y|^{-1} |y|` +- (Logarithmic) absolute error = `\log_b |\hat y - y|` +- (Logarithmic) relative error = `\log_b |\hat y - y| - \log_b |y|` +- (Logarithmic) absolute accuracy = `-\log_b |\hat y - y|` +- (Logarithmic) relative accuracy = `-\log_b |\hat y - y| + \log_b |y|` + +where `b = 2` and `b = 10` for bits and digits respectively. Note that: + +- The logarithmic error roughly equals the position of the first incorrect bit or digit +- The logarithmic accuracy roughly equals the number of correct bits or digits in the result + +These definitions also hold for complex numbers, using `|a+bi| = \sqrt{a^2+b^2}`. + +*Full accuracy* means that the accuracy of a result at least equals *prec*-1, i.e. it is correct except possibly for the last bit. + +Representation of numbers +------------------------- + +Mpmath uses binary arithmetic. A binary floating-point number is a number of the form `man \times 2^{exp}` where both *man* (the *mantissa*) and *exp* (the *exponent*) are integers. Some examples of floating-point numbers are given in the following table. + + +--------+----------+----------+ + | Number | Mantissa | Exponent | + +========+==========+==========+ + | 3 | 3 | 0 | + +--------+----------+----------+ + | 10 | 5 | 1 | + +--------+----------+----------+ + | -16 | -1 | 4 | + +--------+----------+----------+ + | 1.25 | 5 | -2 | + +--------+----------+----------+ + +The representation as defined so far is not unique; one can always multiply the mantissa by 2 and subtract 1 from the exponent with no change in the numerical value. In mpmath, numbers are always normalized so that *man* is an odd number, with the exception of zero which is always taken to have *man = exp = 0*. With these conventions, every representable number has a unique representation. (Mpmath does not currently distinguish between positive and negative zero.) + +Simple mathematical operations are now easy to define. Due to uniqueness, equality testing of two numbers simply amounts to separately checking equality of the mantissas and the exponents. Multiplication of nonzero numbers is straightforward: `(m 2^e) \times (n 2^f) = (m n) \times 2^{e+f}`. Addition is a bit more involved: we first need to multiply the mantissa of one of the operands by a suitable power of 2 to obtain equal exponents. + +More technically, mpmath represents a floating-point number as a 4-tuple *(sign, man, exp, bc)* where *sign* is 0 or 1 (indicating positive vs negative) and the mantissa is nonnegative; *bc* (*bitcount*) is the size of the absolute value of the mantissa as measured in bits. Though redundant, keeping a separate sign field and explicitly keeping track of the bitcount significantly speeds up arithmetic (the bitcount, especially, is frequently needed but slow to compute from scratch due to the lack of a Python built-in function for the purpose). + +Contrary to popular belief, floating-point *numbers* do not come with an inherent "small uncertainty", although floating-point *arithmetic* generally is inexact. Every binary floating-point number is an exact rational number. With arbitrary-precision integers used for the mantissa and exponent, floating-point numbers can be added, subtracted and multiplied *exactly*. In particular, integers and integer multiples of 1/2, 1/4, 1/8, 1/16, etc. can be represented, added and multiplied exactly in binary floating-point arithmetic. + +Floating-point arithmetic is generally approximate because the size of the mantissa must be limited for efficiency reasons. The maximum allowed width (bitcount) of the mantissa is called the precision or *prec* for short. Sums and products of floating-point numbers are exact as long as the absolute value of the mantissa is smaller than `2^{prec}`. As soon as the mantissa becomes larger than this, it is truncated to contain at most *prec* bits (the exponent is incremented accordingly to preserve the magnitude of the number), and this operation introduces a rounding error. Division is also generally inexact; although we can add and multiply exactly by setting the precision high enough, no precision is high enough to represent for example 1/3 exactly (the same obviously applies for roots, trigonometric functions, etc). + +The special numbers ``+inf``, ``-inf`` and ``nan`` are represented internally by a zero mantissa and a nonzero exponent. + +Mpmath uses arbitrary precision integers for both the mantissa and the exponent, so numbers can be as large in magnitude as permitted by the computer's memory. Some care may be necessary when working with extremely large numbers. Although standard arithmetic operators are safe, it is for example futile to attempt to compute the exponential function of of `10^{100000}`. Mpmath does not complain when asked to perform such a calculation, but instead chugs away on the problem to the best of its ability, assuming that computer resources are infinite. In the worst case, this will be slow and allocate a huge amount of memory; if entirely impossible Python will at some point raise ``OverflowError: long int too large to convert to int``. + +For further details on how the arithmetic is implemented, refer to the mpmath source code. The basic arithmetic operations are found in the ``libmp`` directory; many functions there are commented extensively. + +Decimal issues +-------------- + +Mpmath uses binary arithmetic internally, while most interaction with the user is done via the decimal number system. Translating between binary and decimal numbers is a somewhat subtle matter; many Python novices run into the following "bug" (addressed in the `General Python FAQ `_):: + + >>> 1.2 - 1.0 + 0.19999999999999996 + +Decimal fractions fall into the category of numbers that generally cannot be represented exactly in binary floating-point form. For example, none of the numbers 0.1, 0.01, 0.001 has an exact representation as a binary floating-point number. Although mpmath can approximate decimal fractions with any accuracy, it does not solve this problem for all uses; users who need *exact* decimal fractions should look at the ``decimal`` module in Python's standard library (or perhaps use fractions, which are much faster). + +With *prec* bits of precision, an arbitrary number can be approximated relatively to within `2^{-prec}`, or within `10^{-dps}` for *dps* decimal digits. The equivalent values for *prec* and *dps* are therefore related proportionally via the factor `C = \log(10)/\log(2)`, or roughly 3.32. For example, the standard (binary) precision in mpmath is 53 bits, which corresponds to a decimal precision of 15.95 digits. + +More precisely, mpmath uses the following formulas to translate between *prec* and *dps*:: + + dps(prec) = max(1, int(round(int(prec) / C - 1))) + + prec(dps) = max(1, int(round((int(dps) + 1) * C))) + +Note that the dps is set 1 decimal digit lower than the corresponding binary precision. This is done to hide minor rounding errors and artifacts resulting from binary-decimal conversion. As a result, mpmath interprets 53 bits as giving 15 digits of decimal precision, not 16. + +The *dps* value controls the number of digits to display when printing numbers with :func:`str`, while the decimal precision used by :func:`repr` is set two or three digits higher. For example, with 15 dps we have:: + + >>> from mpmath import * + >>> mp.dps = 15 + >>> str(pi) + '3.14159265358979' + >>> repr(+pi) + "mpf('3.1415926535897931')" + +The extra digits in the output from ``repr`` ensure that ``x == eval(repr(x))`` holds, i.e. that numbers can be converted to strings and back losslessly. + +It should be noted that precision and accuracy do not always correlate when translating between binary and decimal. As a simple example, the number 0.1 has a decimal precision of 1 digit but is an infinitely accurate representation of 1/10. Conversely, the number `2^{-50}` has a binary representation with 1 bit of precision that is infinitely accurate; the same number can actually be represented exactly as a decimal, but doing so requires 35 significant digits:: + + 0.00000000000000088817841970012523233890533447265625 + +All binary floating-point numbers can be represented exactly as decimals (possibly requiring many digits), but the converse is false. + +Correctness guarantees +---------------------- + +Basic arithmetic operations (with the ``mp`` context) are always performed with correct rounding. Results that can be represented exactly are guranteed to be exact, and results from single inexact operations are guaranteed to be the best possible rounded values. For higher-level operations, mpmath does not generally guarantee correct rounding. In general, mpmath only guarantees that it will use at least the user-set precision to perform a given calculation. *The user may have to manually set the working precision higher than the desired accuracy for the result, possibly much higher.* + +Functions for evaluation of transcendental functions, linear algebra operations, numerical integration, etc., usually automatically increase the working precision and use a stricter tolerance to give a correctly rounded result with high probability: for example, at 50 bits the temporary precision might be set to 70 bits and the tolerance might be set to 60 bits. It can often be assumed that such functions return values that have full accuracy, given inputs that are exact (or sufficiently precise approximations of exact values), but the user must exercise judgement about whether to trust mpmath. + +The level of rigor in mpmath covers the entire spectrum from "always correct by design" through "nearly always correct" and "handling the most common errors" to "just computing blindly and hoping for the best". Of course, a long-term development goal is to successively increase the rigor where possible. The following list might give an idea of the current state. + +Operations that are correctly rounded: + +* Addition, subtraction and multiplication of real and complex numbers. +* Division and square roots of real numbers. +* Powers of real numbers, assuming sufficiently small integer exponents (huge powers are rounded in the right direction, but possibly farther than necessary). +* Conversion from decimal to binary, for reasonably sized numbers (roughly between `10^{-100}` and `10^{100}`). +* Typically, transcendental functions for exact input-output pairs. + +Operations that should be fully accurate (however, the current implementation may be based on a heuristic error analysis): + +* Radix conversion (large or small numbers). +* Mathematical constants like `\pi`. +* Both real and imaginary parts of exp, cos, sin, cosh, sinh, log. +* Other elementary functions (the largest of the real and imaginary part). +* The gamma and log-gamma functions (the largest of the real and the imaginary part; both, when close to real axis). +* Some functions based on hypergeometric series (the largest of the real and imaginary part). + +Correctness of root-finding, numerical integration, etc. largely depends on the well-behavedness of the input functions. Specific limitations are sometimes noted in the respective sections of the documentation. + +Double precision emulation +-------------------------- + +On most systems, Python's ``float`` type represents an IEEE 754 *double precision* number, with a precision of 53 bits and rounding-to-nearest. With default precision (``mp.prec = 53``), the mpmath ``mpf`` type roughly emulates the behavior of the ``float`` type. Sources of incompatibility include the following: + +* In hardware floating-point arithmetic, the size of the exponent is restricted to a fixed range: regular Python floats have a range between roughly `10^{-300}` and `10^{300}`. Mpmath does not emulate overflow or underflow when exponents fall outside this range. +* On some systems, Python uses 80-bit (extended double) registers for floating-point operations. Due to double rounding, this makes the ``float`` type less accurate. This problem is only known to occur with Python versions compiled with GCC on 32-bit systems. +* Machine floats very close to the exponent limit round subnormally, meaning that they lose accuracy (Python may raise an exception instead of rounding a ``float`` subnormally). +* Mpmath is able to produce more accurate results for transcendental functions. + +Further reading +--------------- + +There are many excellent textbooks on numerical analysis and floating-point arithmetic. Some good web resources are: + +* `David Goldberg, What Every Computer Scientist Should Know About Floating-Point Arithmetic `_ +* `The Wikipedia article about numerical analysis `_ diff --git a/latest/_sources/modules/series.txt b/latest/_sources/modules/series.txt new file mode 100644 index 000000000000..849f17c7c87b --- /dev/null +++ b/latest/_sources/modules/series.txt @@ -0,0 +1,188 @@ +Series Expansions +================= + +.. module:: sympy.series + +The series module implements series expansions as a function and many related +functions. + +Limits +------ + +The main purpose of this module is the computation of limits. + +.. autofunction:: sympy.series.limits.limit + +.. autoclass:: sympy.series.limits.Limit + :members: + +As is explained above, the workhorse for limit computations is the +function gruntz() which implements Gruntz' algorithm for computing limits. + +The Gruntz Algorithm +^^^^^^^^^^^^^^^^^^^^ + +This section explains the basics of the algorithm used for computing limits. +Most of the time the limit() function should just work. However it is still +useful to keep in mind how it is implemented in case something does not work +as expected. + +First we define an ordering on functions. Suppose `f(x)` and `g(x)` are two +real-valued functions such that `\lim_{x \to \infty} f(x) = \infty` and +similarly `\lim_{x \to \infty} g(x) = \infty`. We shall say that `f(x)` +*dominates* +`g(x)`, written `f(x) \succ g(x)`, if for all `a, b \in \mathbb{R}_{>0}` we have +`\lim_{x \to \infty} \frac{f(x)^a}{g(x)^b} = \infty`. +We also say that `f(x)` and +`g(x)` are *of the same comparability class* if neither `f(x) \succ g(x)` nor +`g(x) \succ f(x)` and shall denote it as `f(x) \asymp g(x)`. + +Note that whenever `a, b \in \mathbb{R}_{>0}` then +`a f(x)^b \asymp f(x)`, and we shall use this to extend the definition of +`\succ` to all functions which tend to `0` or `\pm \infty` as `x \to \infty`. +Thus we declare that `f(x) \asymp 1/f(x)` and `f(x) \asymp -f(x)`. + +It is easy to show the following examples: + +* `e^x \succ x^m` +* `e^{x^2} \succ e^{mx}` +* `e^{e^x} \succ e^{x^m}` +* `x^m \asymp x^n` +* `e^{x + \frac{1}{x}} \asymp e^{x + \log{x}} \asymp e^x`. + +From the above definition, it is possible to prove the following property: + + Suppose `\omega`, `g_1, g_2, \dots` are functions of `x`, + `\lim_{x \to \infty} \omega = 0` and `\omega \succ g_i` for + all `i`. Let `c_1, c_2, \dots \in \mathbb{R}` with `c_1 < c_2 < \dots`. + + Then `\lim_{x \to \infty} \sum_i g_i \omega^{c_i} = \lim_{x \to \infty} g_1 \omega^{c_1}`. + +For `g_1 = g` and `\omega` as above we also have the following easy result: + + * `\lim_{x \to \infty} g \omega^c = 0` for `c > 0` + * `\lim_{x \to \infty} g \omega^c = \pm \infty` for `c < 0`, + where the sign is determined by the (eventual) sign of `g` + * `\lim_{x \to \infty} g \omega^0 = \lim_{x \to \infty} g`. + + +Using these results yields the following strategy for computing +`\lim_{x \to \infty} f(x)`: + +1. Find the set of *most rapidly varying subexpressions* (MRV set) of `f(x)`. + That is, from the set of all subexpressions of `f(x)`, find the elements that + are maximal under the relation `\succ`. +2. Choose a function `\omega` that is in the same comparability class as + the elements in the MRV set, such that `\lim_{x \to \infty} \omega = 0`. +3. Expand `f(x)` as a series in `\omega` in such a way that the antecedents of + the above theorem are satisfied. +4. Apply the theorem and conclude the computation of + `\lim_{x \to \infty} f(x)`, possibly by recursively working on `g_1(x)`. + + +Notes +""""" + +This exposition glossed over several details. Many are described in the file +gruntz.py, and all can be found in Gruntz' very readable thesis. The most +important points that have not been explained are: + +1. Given f(x) and g(x), how do we determine if `f(x) \succ g(x)`, + `g(x) \succ f(x)` or `g(x) \asymp f(x)`? +2. How do we find the MRV set of an expression? +3. How do we compute series expansions? +4. Why does the algorithm terminate? + +If you are interested, be sure to take a look at +`Gruntz Thesis `_. + +Reference +""""""""" + +.. autofunction:: sympy.series.gruntz.gruntz + +.. autofunction:: sympy.series.gruntz.compare + +.. autofunction:: sympy.series.gruntz.rewrite + +.. autofunction:: sympy.series.gruntz.build_expression_tree + +.. autofunction:: sympy.series.gruntz.mrv_leadterm + +.. autofunction:: sympy.series.gruntz.calculate_series + +.. autofunction:: sympy.series.gruntz.limitinf + +.. autofunction:: sympy.series.gruntz.sign + +.. autofunction:: sympy.series.gruntz.mrv + +.. autofunction:: sympy.series.gruntz.mrv_max1 + +.. autofunction:: sympy.series.gruntz.mrv_max3 + +.. autoclass:: sympy.series.gruntz.SubsSet + :members: + + +More Intuitive Series Expansion +------------------------------- + +This is achieved +by creating a wrapper around Basic.series(). This allows for the use of +series(x*cos(x),x), which is possibly more intuative than (x*cos(x)).series(x). + +Examples +^^^^^^^^ + >>> from sympy import Symbol, cos, series + >>> x = Symbol('x') + >>> series(cos(x),x) + 1 - x**2/2 + x**4/24 + O(x**6) + +Reference +^^^^^^^^^ + +.. autofunction:: sympy.series.series.series + +Order Terms +----------- + +This module also implements automatic keeping track of the order of your +expansion. + +Examples +^^^^^^^^ + >>> from sympy import Symbol, Order + >>> x = Symbol('x') + >>> Order(x) + x**2 + O(x) + >>> Order(x) + 1 + 1 + O(x) + +Reference +^^^^^^^^^ + +.. autoclass:: sympy.series.order.Order + :members: + +Series Acceleration +------------------- + +TODO + +Reference +^^^^^^^^^ + +.. autofunction:: sympy.series.acceleration.richardson + +.. autofunction:: sympy.series.acceleration.shanks + +Residues +-------- + +TODO + +Reference +^^^^^^^^^ + +.. autofunction:: sympy.series.residues.residue diff --git a/latest/_static/default.css b/latest/_static/default.css index cd23c6959fdd..56b201403c6e 100644 --- a/latest/_static/default.css +++ b/latest/_static/default.css @@ -10,7 +10,7 @@ */ @import url("basic.css"); -@import url(http://fonts.googleapis.com/css?family=Open+Sans); +@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400); /* -- page layout ----------------------------------------------------------- */ @@ -285,4 +285,135 @@ code{ background-color: #ECF0F3; border-radius: 3px; padding: 2px; -} \ No newline at end of file +} + +/* +popupmenu css +*/ +#popupmenu { + position: relative; + margin: 0; + font-family: 'Open Sans'; + line-height: 1; + width: 100%; +} +.align-right { + float: right; +} +#popupmenu ul { + margin: 0; + padding: 0; + list-style: none; + display: block; +} +#popupmenu ul li { + position: relative; + margin: 0; + padding: 0; +} +#popupmenu ul li a { + text-decoration: none; + cursor: pointer; +} +#popupmenu > ul > li > a { + color: #dddddd; + display: block; + padding: 20px; + border-top: 1px solid #000000; + border-left: 1px solid #000000; + border-right: 1px solid #000000; + background: #567e35; + letter-spacing: 1px; + font-size: 16px; + font-weight: 400; + position: relative; +} +#popupmenu > ul > li:first-child > a { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +#popupmenu > ul > li:last-child > a { + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; + border-bottom: 1px solid #000000; +} +#popupmenu > ul > li:hover > a, +#popupmenu > ul > li.open > a, +#popupmenu > ul > li.active > a { + background: #62903c; + color: #ffffff; +} +#popupmenu ul > ul > li.has-sub > a::after { + content: ""; + position: absolute; + display: block; +} +#popupmenu ul > li.has-sub > a::before { + content: ""; + position: absolute; + display: block; +} +#popupmenu ul > li.has-sub::after { + content: ""; + display: visible; + position: absolute; + border: 7px solid transparent; + border-top-color: #dddddd; + right: 20px; + top: 24.5px; +} +#popupmenu ul > li:hover::after, +#popupmenu ul > li.active::after, +#popupmenu ul > li.open::after { + border-top-color: #ffffff; +} +#popupmenu ul > li.has-sub.open > a::after { + opacity: 1; + bottom: -13px; +} +#popupmenu ul > li.has-sub.open > a::before { + opacity: 1; + bottom: -12px; +} +#popupmenu ul ul { + display: none; +} +#popupmenu ul ul li { + border-left: 1px solid #ccc; + border-right: 1px solid #ccc; +} +#popupmenu ul ul li a { + background: #f1f1f1; + display: block; + position: relative; + font-size: 15px; + padding: 14px 20px; + border-bottom: 1px solid #dddddd; + color: #777777; + font-weight: 300; +} +#popupmenu ul ul li:first-child > a { + padding-top: 18px; +} +#popupmenu ul ul li:hover > a, +#popupmenu ul ul li.open > a, +#popupmenu ul ul li.active > a { + background: #e4e4e4; + color: #666666; +} +#popupmenu ul ul > li.has-sub > a::after { + border-top: 13px solid #dddddd; +} +#popupmenu ul ul > li.has-sub > a::before { + border-top: 13px solid #e4e4e4; +} +#popupmenu ul ul > li.has-sub::after { + top: 18.5px; + border-width: 6px; + border-top-color: #777777; +} +#popupmenu ul ul > li:hover::after, +#popupmenu ul ul > li.active::after, +#popupmenu ul ul > li.open::after { + border-top-color: #666666; +} diff --git a/latest/index.html b/latest/index.html index ecb5cb5fea42..07a1e882b1a2 100644 --- a/latest/index.html +++ b/latest/index.html @@ -1,21 +1,19 @@ - - - - + Welcome to SymPy’s documentation! — SymPy 1.0 documentation - + - + + - + +